src/pyams_skin/resources/js/ext/jquery-dataTables-fixedHeader.js
changeset 557 bca7a7e058a3
equal deleted inserted replaced
-1:000000000000 557:bca7a7e058a3
       
     1 /*! FixedHeader 2.1.0
       
     2  * ©2010-2014 SpryMedia Ltd - datatables.net/license
       
     3  */
       
     4 
       
     5 /**
       
     6  * @summary     FixedHeader
       
     7  * @description Fix a table's header or footer, so it is always visible while
       
     8  *              Scrolling
       
     9  * @version     2.1.0
       
    10  * @file        dataTables.fixedHeader.js
       
    11  * @author      SpryMedia Ltd (www.sprymedia.co.uk)
       
    12  * @contact     www.sprymedia.co.uk/contact
       
    13  * @copyright   Copyright 2009-2014 SpryMedia Ltd.
       
    14  *
       
    15  * This source file is free software, available under the following license:
       
    16  *   MIT license - http://datatables.net/license/mit
       
    17  *
       
    18  * This source file is distributed in the hope that it will be useful, but
       
    19  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
       
    20  * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
       
    21  *
       
    22  * For details please refer to: http://www.datatables.net
       
    23  */
       
    24 
       
    25 /* Global scope for FixedColumns for backwards compatibility - will be removed
       
    26  * in future. Not documented in 1.1.x.
       
    27  */
       
    28 
       
    29 /* Global scope for FixedColumns */
       
    30 var FixedHeader;
       
    31 
       
    32 (function (window, document, undefined) {
       
    33 
       
    34 
       
    35 	var factory = function ($, DataTable) {
       
    36 		"use strict";
       
    37 
       
    38 		/*
       
    39 		 * Function: FixedHeader
       
    40 		 * Purpose:  Provide 'fixed' header, footer and columns on an HTML table
       
    41 		 * Returns:  object:FixedHeader - must be called with 'new'
       
    42 		 * Inputs:   mixed:mTable - target table
       
    43 		 *					   1. DataTable object - when using FixedHeader with DataTables, or
       
    44 		 *					   2. HTML table node - when using FixedHeader without DataTables
       
    45 		 *           object:oInit - initialisation settings, with the following properties (each optional)
       
    46 		 *             bool:top -    fix the header (default true)
       
    47 		 *             bool:bottom - fix the footer (default false)
       
    48 		 *             int:left -    fix the left column(s) (default 0)
       
    49 		 *             int:right -   fix the right column(s) (default 0)
       
    50 		 *             int:zTop -    fixed header zIndex
       
    51 		 *             int:zBottom - fixed footer zIndex
       
    52 		 *             int:zLeft -   fixed left zIndex
       
    53 		 *             int:zRight -  fixed right zIndex
       
    54 		 */
       
    55 		FixedHeader = function (mTable, oInit) {
       
    56 			/* Sanity check - you just know it will happen */
       
    57 			if (!this instanceof FixedHeader) {
       
    58 				alert("FixedHeader warning: FixedHeader must be initialised with the 'new' keyword.");
       
    59 				return;
       
    60 			}
       
    61 
       
    62 			var that = this;
       
    63 			var oSettings = {
       
    64 				"aoCache": [],
       
    65 				"oSides": {
       
    66 					"top": true,
       
    67 					"bottom": false,
       
    68 					"left": 0,
       
    69 					"right": 0
       
    70 				},
       
    71 				"oZIndexes": {
       
    72 					"top": 104,
       
    73 					"bottom": 103,
       
    74 					"left": 102,
       
    75 					"right": 101
       
    76 				},
       
    77 				"oCloneOnDraw": {
       
    78 					"top": false,
       
    79 					"bottom": false,
       
    80 					"left": true,
       
    81 					"right": true
       
    82 				},
       
    83 				"oMes": {
       
    84 					"iTableWidth": 0,
       
    85 					"iTableHeight": 0,
       
    86 					"iTableLeft": 0,
       
    87 					"iTableRight": 0, /* note this is left+width, not actually "right" */
       
    88 					"iTableTop": 0,
       
    89 					"iTableBottom": 0 /* note this is top+height, not actually "bottom" */
       
    90 				},
       
    91 				"oOffset": {
       
    92 					"top": 0
       
    93 				},
       
    94 				"nTable": null,
       
    95 				"bFooter": false,
       
    96 				"bInitComplete": false
       
    97 			};
       
    98 
       
    99 			/*
       
   100 			 * Function: fnGetSettings
       
   101 			 * Purpose:  Get the settings for this object
       
   102 			 * Returns:  object: - settings object
       
   103 			 * Inputs:   -
       
   104 			 */
       
   105 			this.fnGetSettings = function () {
       
   106 				return oSettings;
       
   107 			};
       
   108 
       
   109 			/*
       
   110 			 * Function: fnUpdate
       
   111 			 * Purpose:  Update the positioning and copies of the fixed elements
       
   112 			 * Returns:  -
       
   113 			 * Inputs:   -
       
   114 			 */
       
   115 			this.fnUpdate = function () {
       
   116 				this._fnUpdateClones();
       
   117 				this._fnUpdatePositions();
       
   118 			};
       
   119 
       
   120 			/*
       
   121 			 * Function: fnPosition
       
   122 			 * Purpose:  Update the positioning of the fixed elements
       
   123 			 * Returns:  -
       
   124 			 * Inputs:   -
       
   125 			 */
       
   126 			this.fnPosition = function () {
       
   127 				this._fnUpdatePositions();
       
   128 			};
       
   129 
       
   130 
       
   131 			var dt = $.fn.dataTable.Api ?
       
   132 				new $.fn.dataTable.Api(mTable).settings()[0] :
       
   133 				mTable.fnSettings();
       
   134 
       
   135 			dt._oPluginFixedHeader = this;
       
   136 
       
   137 			/* Let's do it */
       
   138 			this.fnInit(dt, oInit);
       
   139 
       
   140 		};
       
   141 
       
   142 
       
   143 		/*
       
   144 		 * Variable: FixedHeader
       
   145 		 * Purpose:  Prototype for FixedHeader
       
   146 		 * Scope:    global
       
   147 		 */
       
   148 		FixedHeader.prototype = {
       
   149 			/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
       
   150 			 * Initialisation
       
   151 			 */
       
   152 
       
   153 			/*
       
   154 			 * Function: fnInit
       
   155 			 * Purpose:  The "constructor"
       
   156 			 * Returns:  -
       
   157 			 * Inputs:   {as FixedHeader function}
       
   158 			 */
       
   159 			fnInit: function (oDtSettings, oInit) {
       
   160 				var s = this.fnGetSettings();
       
   161 				var that = this;
       
   162 
       
   163 				/* Record the user definable settings */
       
   164 				this.fnInitSettings(s, oInit);
       
   165 
       
   166 				if (oDtSettings.oScroll.sX !== "" || oDtSettings.oScroll.sY !== "") {
       
   167 					alert("FixedHeader 2 is not supported with DataTables' scrolling mode at this time");
       
   168 					return;
       
   169 				}
       
   170 
       
   171 				s.nTable = oDtSettings.nTable;
       
   172 				oDtSettings.aoDrawCallback.unshift({
       
   173 													   "fn": function () {
       
   174 														   FixedHeader.fnMeasure();
       
   175 														   that._fnUpdateClones.call(that);
       
   176 														   that._fnUpdatePositions.call(that);
       
   177 													   },
       
   178 													   "sName": "FixedHeader"
       
   179 												   });
       
   180 
       
   181 				s.bFooter = ($('>tfoot', s.nTable).length > 0) ? true : false;
       
   182 
       
   183 				/* Add the 'sides' that are fixed */
       
   184 				if (s.oSides.top) {
       
   185 					s.aoCache.push(that._fnCloneTable("fixedHeader", "FixedHeader_Header", that._fnCloneThead));
       
   186 				}
       
   187 				if (s.oSides.bottom) {
       
   188 					s.aoCache.push(that._fnCloneTable("fixedFooter", "FixedHeader_Footer", that._fnCloneTfoot));
       
   189 				}
       
   190 				if (s.oSides.left) {
       
   191 					s.aoCache.push(that._fnCloneTable("fixedLeft", "FixedHeader_Left", that._fnCloneTLeft, s.oSides.left));
       
   192 				}
       
   193 				if (s.oSides.right) {
       
   194 					s.aoCache.push(that._fnCloneTable("fixedRight", "FixedHeader_Right", that._fnCloneTRight, s.oSides.right));
       
   195 				}
       
   196 
       
   197 				/* Event listeners for window movement */
       
   198 				FixedHeader.afnScroll.push(function () {
       
   199 					that._fnUpdatePositions.call(that);
       
   200 				});
       
   201 
       
   202 				$(window).resize(function () {
       
   203 					FixedHeader.fnMeasure();
       
   204 					that._fnUpdateClones.call(that);
       
   205 					that._fnUpdatePositions.call(that);
       
   206 				});
       
   207 
       
   208 				$(s.nTable)
       
   209 					.on('column-reorder', function () {
       
   210 							FixedHeader.fnMeasure();
       
   211 							that._fnUpdateClones(true);
       
   212 							that._fnUpdatePositions();
       
   213 						})
       
   214 					.on('column-visibility', function () {
       
   215 							FixedHeader.fnMeasure();
       
   216 							that._fnUpdateClones(true);
       
   217 							that._fnUpdatePositions();
       
   218 						});
       
   219 
       
   220 				/* Get things right to start with */
       
   221 				FixedHeader.fnMeasure();
       
   222 				that._fnUpdateClones();
       
   223 				that._fnUpdatePositions();
       
   224 
       
   225 				s.bInitComplete = true;
       
   226 			},
       
   227 
       
   228 
       
   229 			/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
       
   230 			 * Support functions
       
   231 			 */
       
   232 
       
   233 			/*
       
   234 			 * Function: fnInitSettings
       
   235 			 * Purpose:  Take the user's settings and copy them to our local store
       
   236 			 * Returns:  -
       
   237 			 * Inputs:   object:s - the local settings object
       
   238 			 *           object:oInit - the user's settings object
       
   239 			 */
       
   240 			fnInitSettings: function (s, oInit) {
       
   241 				if (oInit !== undefined) {
       
   242 					if (oInit.top !== undefined) {
       
   243 						s.oSides.top = oInit.top;
       
   244 					}
       
   245 					if (oInit.bottom !== undefined) {
       
   246 						s.oSides.bottom = oInit.bottom;
       
   247 					}
       
   248 					if (typeof oInit.left == 'boolean') {
       
   249 						s.oSides.left = oInit.left ? 1 : 0;
       
   250 					}
       
   251 					else if (oInit.left !== undefined) {
       
   252 						s.oSides.left = oInit.left;
       
   253 					}
       
   254 					if (typeof oInit.right == 'boolean') {
       
   255 						s.oSides.right = oInit.right ? 1 : 0;
       
   256 					}
       
   257 					else if (oInit.right !== undefined) {
       
   258 						s.oSides.right = oInit.right;
       
   259 					}
       
   260 
       
   261 					if (oInit.zTop !== undefined) {
       
   262 						s.oZIndexes.top = oInit.zTop;
       
   263 					}
       
   264 					if (oInit.zBottom !== undefined) {
       
   265 						s.oZIndexes.bottom = oInit.zBottom;
       
   266 					}
       
   267 					if (oInit.zLeft !== undefined) {
       
   268 						s.oZIndexes.left = oInit.zLeft;
       
   269 					}
       
   270 					if (oInit.zRight !== undefined) {
       
   271 						s.oZIndexes.right = oInit.zRight;
       
   272 					}
       
   273 
       
   274 					if (oInit.offsetTop !== undefined) {
       
   275 						s.oOffset.top = oInit.offsetTop;
       
   276 					}
       
   277 					if (oInit.alwaysCloneTop !== undefined) {
       
   278 						s.oCloneOnDraw.top = oInit.alwaysCloneTop;
       
   279 					}
       
   280 					if (oInit.alwaysCloneBottom !== undefined) {
       
   281 						s.oCloneOnDraw.bottom = oInit.alwaysCloneBottom;
       
   282 					}
       
   283 					if (oInit.alwaysCloneLeft !== undefined) {
       
   284 						s.oCloneOnDraw.left = oInit.alwaysCloneLeft;
       
   285 					}
       
   286 					if (oInit.alwaysCloneRight !== undefined) {
       
   287 						s.oCloneOnDraw.right = oInit.alwaysCloneRight;
       
   288 					}
       
   289 				}
       
   290 			},
       
   291 
       
   292 			/*
       
   293 			 * Function: _fnCloneTable
       
   294 			 * Purpose:  Clone the table node and do basic initialisation
       
   295 			 * Returns:  -
       
   296 			 * Inputs:   -
       
   297 			 */
       
   298 			_fnCloneTable: function (sType, sClass, fnClone, iCells) {
       
   299 				var s = this.fnGetSettings();
       
   300 				var nCTable;
       
   301 
       
   302 				/* We know that the table _MUST_ has a DIV wrapped around it, because this is simply how
       
   303 				 * DataTables works. Therefore, we can set this to be relatively position (if it is not
       
   304 				 * alreadu absolute, and use this as the base point for the cloned header
       
   305 				 */
       
   306 				if ($(s.nTable.parentNode).css('position') != "absolute") {
       
   307 					s.nTable.parentNode.style.position = "relative";
       
   308 				}
       
   309 
       
   310 				/* Just a shallow clone will do - we only want the table node */
       
   311 				nCTable = s.nTable.cloneNode(false);
       
   312 				nCTable.removeAttribute('id');
       
   313 
       
   314 				var nDiv = document.createElement('div');
       
   315 				nDiv.style.position = "absolute";
       
   316 				nDiv.style.top = "0px";
       
   317 				nDiv.style.left = "0px";
       
   318 				nDiv.className += " FixedHeader_Cloned " + sType + " " + sClass;
       
   319 
       
   320 				/* Set the zIndexes */
       
   321 				if (sType == "fixedHeader") {
       
   322 					nDiv.style.zIndex = s.oZIndexes.top;
       
   323 				}
       
   324 				if (sType == "fixedFooter") {
       
   325 					nDiv.style.zIndex = s.oZIndexes.bottom;
       
   326 				}
       
   327 				if (sType == "fixedLeft") {
       
   328 					nDiv.style.zIndex = s.oZIndexes.left;
       
   329 				}
       
   330 				else if (sType == "fixedRight") {
       
   331 					nDiv.style.zIndex = s.oZIndexes.right;
       
   332 				}
       
   333 
       
   334 				/* remove margins since we are going to position it absolutely */
       
   335 				nCTable.style.margin = "0";
       
   336 
       
   337 				/* Insert the newly cloned table into the DOM, on top of the "real" header */
       
   338 				nDiv.appendChild(nCTable);
       
   339 				document.body.appendChild(nDiv);
       
   340 
       
   341 				return {
       
   342 					"nNode": nCTable,
       
   343 					"nWrapper": nDiv,
       
   344 					"sType": sType,
       
   345 					"sPosition": "",
       
   346 					"sTop": "",
       
   347 					"sLeft": "",
       
   348 					"fnClone": fnClone,
       
   349 					"iCells": iCells
       
   350 				};
       
   351 			},
       
   352 
       
   353 			/*
       
   354 			 * Function: _fnMeasure
       
   355 			 * Purpose:  Get the current positioning of the table in the DOM
       
   356 			 * Returns:  -
       
   357 			 * Inputs:   -
       
   358 			 */
       
   359 			_fnMeasure: function () {
       
   360 				var
       
   361 					s = this.fnGetSettings(),
       
   362 					m = s.oMes,
       
   363 					jqTable = $(s.nTable),
       
   364 					oOffset = jqTable.offset(),
       
   365 					iParentScrollTop = this._fnSumScroll(s.nTable.parentNode, 'scrollTop'),
       
   366 					iParentScrollLeft = this._fnSumScroll(s.nTable.parentNode, 'scrollLeft');
       
   367 
       
   368 				m.iTableWidth = jqTable.outerWidth();
       
   369 				m.iTableHeight = jqTable.outerHeight();
       
   370 				m.iTableLeft = oOffset.left + s.nTable.parentNode.scrollLeft;
       
   371 				m.iTableTop = oOffset.top + iParentScrollTop;
       
   372 				m.iTableRight = m.iTableLeft + m.iTableWidth;
       
   373 				m.iTableRight = FixedHeader.oDoc.iWidth - m.iTableLeft - m.iTableWidth;
       
   374 				m.iTableBottom = FixedHeader.oDoc.iHeight - m.iTableTop - m.iTableHeight;
       
   375 			},
       
   376 
       
   377 			/*
       
   378 			 * Function: _fnSumScroll
       
   379 			 * Purpose:  Sum node parameters all the way to the top
       
   380 			 * Returns:  int: sum
       
   381 			 * Inputs:   node:n - node to consider
       
   382 			 *           string:side - scrollTop or scrollLeft
       
   383 			 */
       
   384 			_fnSumScroll: function (n, side) {
       
   385 				var i = n[side];
       
   386 				while (n = n.parentNode) {
       
   387 					if (n.nodeName == 'HTML' || n.nodeName == 'BODY') {
       
   388 						break;
       
   389 					}
       
   390 					i = n[side];
       
   391 				}
       
   392 				return i;
       
   393 			},
       
   394 
       
   395 			/*
       
   396 			 * Function: _fnUpdatePositions
       
   397 			 * Purpose:  Loop over the fixed elements for this table and update their positions
       
   398 			 * Returns:  -
       
   399 			 * Inputs:   -
       
   400 			 */
       
   401 			_fnUpdatePositions: function () {
       
   402 				var s = this.fnGetSettings();
       
   403 				this._fnMeasure();
       
   404 
       
   405 				for (var i = 0, iLen = s.aoCache.length; i < iLen; i++) {
       
   406 					if (s.aoCache[i].sType == "fixedHeader") {
       
   407 						this._fnScrollFixedHeader(s.aoCache[i]);
       
   408 					}
       
   409 					else if (s.aoCache[i].sType == "fixedFooter") {
       
   410 						this._fnScrollFixedFooter(s.aoCache[i]);
       
   411 					}
       
   412 					else if (s.aoCache[i].sType == "fixedLeft") {
       
   413 						this._fnScrollHorizontalLeft(s.aoCache[i]);
       
   414 					}
       
   415 					else {
       
   416 						this._fnScrollHorizontalRight(s.aoCache[i]);
       
   417 					}
       
   418 				}
       
   419 			},
       
   420 
       
   421 			/*
       
   422 			 * Function: _fnUpdateClones
       
   423 			 * Purpose:  Loop over the fixed elements for this table and call their cloning functions
       
   424 			 * Returns:  -
       
   425 			 * Inputs:   -
       
   426 			 */
       
   427 			_fnUpdateClones: function (full) {
       
   428 				var s = this.fnGetSettings();
       
   429 
       
   430 				if (full) {
       
   431 					// This is a little bit of a hack to force a full clone draw. When
       
   432 					// `full` is set to true, we want to reclone the source elements,
       
   433 					// regardless of the clone-on-draw settings
       
   434 					s.bInitComplete = false;
       
   435 				}
       
   436 
       
   437 				for (var i = 0, iLen = s.aoCache.length; i < iLen; i++) {
       
   438 					s.aoCache[i].fnClone.call(this, s.aoCache[i]);
       
   439 				}
       
   440 
       
   441 				if (full) {
       
   442 					s.bInitComplete = true;
       
   443 				}
       
   444 			},
       
   445 
       
   446 
       
   447 			/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
       
   448 			 * Scrolling functions
       
   449 			 */
       
   450 
       
   451 			/*
       
   452 			 * Function: _fnScrollHorizontalLeft
       
   453 			 * Purpose:  Update the positioning of the scrolling elements
       
   454 			 * Returns:  -
       
   455 			 * Inputs:   object:oCache - the cached values for this fixed element
       
   456 			 */
       
   457 			_fnScrollHorizontalRight: function (oCache) {
       
   458 				var
       
   459 					s = this.fnGetSettings(),
       
   460 					oMes = s.oMes,
       
   461 					oWin = FixedHeader.oWin,
       
   462 					oDoc = FixedHeader.oDoc,
       
   463 					nTable = oCache.nWrapper,
       
   464 					iFixedWidth = $(nTable).outerWidth();
       
   465 
       
   466 				if (oWin.iScrollRight < oMes.iTableRight) {
       
   467 					/* Fully right aligned */
       
   468 					this._fnUpdateCache(oCache, 'sPosition', 'absolute', 'position', nTable.style);
       
   469 					this._fnUpdateCache(oCache, 'sTop', oMes.iTableTop + "px", 'top', nTable.style);
       
   470 					this._fnUpdateCache(oCache, 'sLeft', (oMes.iTableLeft + oMes.iTableWidth - iFixedWidth) + "px", 'left', nTable.style);
       
   471 				}
       
   472 				else if (oMes.iTableLeft < oDoc.iWidth - oWin.iScrollRight - iFixedWidth) {
       
   473 					/* Middle */
       
   474 					this._fnUpdateCache(oCache, 'sPosition', 'fixed', 'position', nTable.style);
       
   475 					this._fnUpdateCache(oCache, 'sTop', (oMes.iTableTop - oWin.iScrollTop) + "px", 'top', nTable.style);
       
   476 					this._fnUpdateCache(oCache, 'sLeft', (oWin.iWidth - iFixedWidth) + "px", 'left', nTable.style);
       
   477 				}
       
   478 				else {
       
   479 					/* Fully left aligned */
       
   480 					this._fnUpdateCache(oCache, 'sPosition', 'absolute', 'position', nTable.style);
       
   481 					this._fnUpdateCache(oCache, 'sTop', oMes.iTableTop + "px", 'top', nTable.style);
       
   482 					this._fnUpdateCache(oCache, 'sLeft', oMes.iTableLeft + "px", 'left', nTable.style);
       
   483 				}
       
   484 			},
       
   485 
       
   486 			/*
       
   487 			 * Function: _fnScrollHorizontalLeft
       
   488 			 * Purpose:  Update the positioning of the scrolling elements
       
   489 			 * Returns:  -
       
   490 			 * Inputs:   object:oCache - the cached values for this fixed element
       
   491 			 */
       
   492 			_fnScrollHorizontalLeft: function (oCache) {
       
   493 				var
       
   494 					s = this.fnGetSettings(),
       
   495 					oMes = s.oMes,
       
   496 					oWin = FixedHeader.oWin,
       
   497 					oDoc = FixedHeader.oDoc,
       
   498 					nTable = oCache.nWrapper,
       
   499 					iCellWidth = $(nTable).outerWidth();
       
   500 
       
   501 				if (oWin.iScrollLeft < oMes.iTableLeft) {
       
   502 					/* Fully left align */
       
   503 					this._fnUpdateCache(oCache, 'sPosition', 'absolute', 'position', nTable.style);
       
   504 					this._fnUpdateCache(oCache, 'sTop', oMes.iTableTop + "px", 'top', nTable.style);
       
   505 					this._fnUpdateCache(oCache, 'sLeft', oMes.iTableLeft + "px", 'left', nTable.style);
       
   506 				}
       
   507 				else if (oWin.iScrollLeft < oMes.iTableLeft + oMes.iTableWidth - iCellWidth) {
       
   508 					this._fnUpdateCache(oCache, 'sPosition', 'fixed', 'position', nTable.style);
       
   509 					this._fnUpdateCache(oCache, 'sTop', (oMes.iTableTop - oWin.iScrollTop) + "px", 'top', nTable.style);
       
   510 					this._fnUpdateCache(oCache, 'sLeft', "0px", 'left', nTable.style);
       
   511 				}
       
   512 				else {
       
   513 					/* Fully right align */
       
   514 					this._fnUpdateCache(oCache, 'sPosition', 'absolute', 'position', nTable.style);
       
   515 					this._fnUpdateCache(oCache, 'sTop', oMes.iTableTop + "px", 'top', nTable.style);
       
   516 					this._fnUpdateCache(oCache, 'sLeft', (oMes.iTableLeft + oMes.iTableWidth - iCellWidth) + "px", 'left', nTable.style);
       
   517 				}
       
   518 			},
       
   519 
       
   520 			/*
       
   521 			 * Function: _fnScrollFixedFooter
       
   522 			 * Purpose:  Update the positioning of the scrolling elements
       
   523 			 * Returns:  -
       
   524 			 * Inputs:   object:oCache - the cached values for this fixed element
       
   525 			 */
       
   526 			_fnScrollFixedFooter: function (oCache) {
       
   527 				var
       
   528 					s = this.fnGetSettings(),
       
   529 					oMes = s.oMes,
       
   530 					oWin = FixedHeader.oWin,
       
   531 					oDoc = FixedHeader.oDoc,
       
   532 					nTable = oCache.nWrapper,
       
   533 					iTheadHeight = $("thead", s.nTable).outerHeight(),
       
   534 					iCellHeight = $(nTable).outerHeight();
       
   535 
       
   536 				if (oWin.iScrollBottom < oMes.iTableBottom) {
       
   537 					/* Below */
       
   538 					this._fnUpdateCache(oCache, 'sPosition', 'absolute', 'position', nTable.style);
       
   539 					this._fnUpdateCache(oCache, 'sTop', (oMes.iTableTop + oMes.iTableHeight - iCellHeight) + "px", 'top', nTable.style);
       
   540 					this._fnUpdateCache(oCache, 'sLeft', oMes.iTableLeft + "px", 'left', nTable.style);
       
   541 				}
       
   542 				else if (oWin.iScrollBottom < oMes.iTableBottom + oMes.iTableHeight - iCellHeight - iTheadHeight) {
       
   543 					this._fnUpdateCache(oCache, 'sPosition', 'fixed', 'position', nTable.style);
       
   544 					this._fnUpdateCache(oCache, 'sTop', (oWin.iHeight - iCellHeight) + "px", 'top', nTable.style);
       
   545 					this._fnUpdateCache(oCache, 'sLeft', (oMes.iTableLeft - oWin.iScrollLeft) + "px", 'left', nTable.style);
       
   546 				}
       
   547 				else {
       
   548 					/* Above */
       
   549 					this._fnUpdateCache(oCache, 'sPosition', 'absolute', 'position', nTable.style);
       
   550 					this._fnUpdateCache(oCache, 'sTop', (oMes.iTableTop + iCellHeight) + "px", 'top', nTable.style);
       
   551 					this._fnUpdateCache(oCache, 'sLeft', oMes.iTableLeft + "px", 'left', nTable.style);
       
   552 				}
       
   553 			},
       
   554 
       
   555 			/*
       
   556 			 * Function: _fnScrollFixedHeader
       
   557 			 * Purpose:  Update the positioning of the scrolling elements
       
   558 			 * Returns:  -
       
   559 			 * Inputs:   object:oCache - the cached values for this fixed element
       
   560 			 */
       
   561 			_fnScrollFixedHeader: function (oCache) {
       
   562 				var
       
   563 					s = this.fnGetSettings(),
       
   564 					oMes = s.oMes,
       
   565 					oWin = FixedHeader.oWin,
       
   566 					oDoc = FixedHeader.oDoc,
       
   567 					nTable = oCache.nWrapper,
       
   568 					iTbodyHeight = 0,
       
   569 					anTbodies = s.nTable.getElementsByTagName('tbody');
       
   570 
       
   571 				for (var i = 0; i < anTbodies.length; ++i) {
       
   572 					iTbodyHeight += anTbodies[i].offsetHeight;
       
   573 				}
       
   574 
       
   575 				if (oMes.iTableTop > oWin.iScrollTop + s.oOffset.top) {
       
   576 					/* Above the table */
       
   577 					this._fnUpdateCache(oCache, 'sPosition', "absolute", 'position', nTable.style);
       
   578 					this._fnUpdateCache(oCache, 'sTop', oMes.iTableTop + "px", 'top', nTable.style);
       
   579 					this._fnUpdateCache(oCache, 'sLeft', oMes.iTableLeft + "px", 'left', nTable.style);
       
   580 				}
       
   581 				else if (oWin.iScrollTop + s.oOffset.top > oMes.iTableTop + iTbodyHeight) {
       
   582 					/* At the bottom of the table */
       
   583 					this._fnUpdateCache(oCache, 'sPosition', "absolute", 'position', nTable.style);
       
   584 					this._fnUpdateCache(oCache, 'sTop', (oMes.iTableTop + iTbodyHeight) + "px", 'top', nTable.style);
       
   585 					this._fnUpdateCache(oCache, 'sLeft', oMes.iTableLeft + "px", 'left', nTable.style);
       
   586 				}
       
   587 				else {
       
   588 					/* In the middle of the table */
       
   589 					this._fnUpdateCache(oCache, 'sPosition', 'fixed', 'position', nTable.style);
       
   590 					this._fnUpdateCache(oCache, 'sTop', s.oOffset.top + "px", 'top', nTable.style);
       
   591 					this._fnUpdateCache(oCache, 'sLeft', (oMes.iTableLeft - oWin.iScrollLeft) + "px", 'left', nTable.style);
       
   592 				}
       
   593 			},
       
   594 
       
   595 			/*
       
   596 			 * Function: _fnUpdateCache
       
   597 			 * Purpose:  Check the cache and update cache and value if needed
       
   598 			 * Returns:  -
       
   599 			 * Inputs:   object:oCache - local cache object
       
   600 			 *           string:sCache - cache property
       
   601 			 *           string:sSet - value to set
       
   602 			 *           string:sProperty - object property to set
       
   603 			 *           object:oObj - object to update
       
   604 			 */
       
   605 			_fnUpdateCache: function (oCache, sCache, sSet, sProperty, oObj) {
       
   606 				if (oCache[sCache] != sSet) {
       
   607 					oObj[sProperty] = sSet;
       
   608 					oCache[sCache] = sSet;
       
   609 				}
       
   610 			},
       
   611 
       
   612 
       
   613 			/**
       
   614 			 * Copy the classes of all child nodes from one element to another. This implies
       
   615 			 * that the two have identical structure - no error checking is performed to that
       
   616 			 * fact.
       
   617 			 *  @param {element} source Node to copy classes from
       
   618 			 *  @param {element} dest Node to copy classes too
       
   619 			 */
       
   620 			_fnClassUpdate: function (source, dest) {
       
   621 				var that = this;
       
   622 
       
   623 				if (source.nodeName.toUpperCase() === "TR" || source.nodeName.toUpperCase() === "TH" ||
       
   624 					source.nodeName.toUpperCase() === "TD" || source.nodeName.toUpperCase() === "SPAN") {
       
   625 					dest.className = source.className;
       
   626 				}
       
   627 
       
   628 				$(source).children().each(function (i) {
       
   629 					that._fnClassUpdate($(source).children()[i], $(dest).children()[i]);
       
   630 				});
       
   631 			},
       
   632 
       
   633 
       
   634 			/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
       
   635 			 * Cloning functions
       
   636 			 */
       
   637 
       
   638 			/*
       
   639 			 * Function: _fnCloneThead
       
   640 			 * Purpose:  Clone the thead element
       
   641 			 * Returns:  -
       
   642 			 * Inputs:   object:oCache - the cached values for this fixed element
       
   643 			 */
       
   644 			_fnCloneThead: function (oCache) {
       
   645 				var s = this.fnGetSettings();
       
   646 				var nTable = oCache.nNode;
       
   647 
       
   648 				if (s.bInitComplete && !s.oCloneOnDraw.top) {
       
   649 					this._fnClassUpdate($('thead', s.nTable)[0], $('thead', nTable)[0]);
       
   650 					return;
       
   651 				}
       
   652 
       
   653 				/* Set the wrapper width to match that of the cloned table */
       
   654 				var iDtWidth = $(s.nTable).outerWidth();
       
   655 				oCache.nWrapper.style.width = iDtWidth + "px";
       
   656 				nTable.style.width = iDtWidth + "px";
       
   657 
       
   658 				/* Remove any children the cloned table has */
       
   659 				while (nTable.childNodes.length > 0) {
       
   660 					$('thead th', nTable).unbind('click');
       
   661 					nTable.removeChild(nTable.childNodes[0]);
       
   662 				}
       
   663 
       
   664 				/* Clone the DataTables header */
       
   665 				var nThead = $('thead', s.nTable).clone(true)[0];
       
   666 				nTable.appendChild(nThead);
       
   667 
       
   668 				/* Copy the widths across - apparently a clone isn't good enough for this */
       
   669 				var a = [];
       
   670 				var b = [];
       
   671 
       
   672 				$("thead>tr th", s.nTable).each(function (i) {
       
   673 					a.push($(this).width());
       
   674 				});
       
   675 
       
   676 				$("thead>tr td", s.nTable).each(function (i) {
       
   677 					b.push($(this).width());
       
   678 				});
       
   679 
       
   680 				$("thead>tr th", s.nTable).each(function (i) {
       
   681 					$("thead>tr th:eq(" + i + ")", nTable).width(a[i]);
       
   682 					$(this).width(a[i]);
       
   683 				});
       
   684 
       
   685 				$("thead>tr td", s.nTable).each(function (i) {
       
   686 					$("thead>tr td:eq(" + i + ")", nTable).width(b[i]);
       
   687 					$(this).width(b[i]);
       
   688 				});
       
   689 
       
   690 				// Stop DataTables 1.9 from putting a focus ring on the headers when
       
   691 				// clicked to sort
       
   692 				$('th.sorting, th.sorting_desc, th.sorting_asc', nTable).bind('click', function () {
       
   693 					this.blur();
       
   694 				});
       
   695 			},
       
   696 
       
   697 			/*
       
   698 			 * Function: _fnCloneTfoot
       
   699 			 * Purpose:  Clone the tfoot element
       
   700 			 * Returns:  -
       
   701 			 * Inputs:   object:oCache - the cached values for this fixed element
       
   702 			 */
       
   703 			_fnCloneTfoot: function (oCache) {
       
   704 				var s = this.fnGetSettings();
       
   705 				var nTable = oCache.nNode;
       
   706 
       
   707 				/* Set the wrapper width to match that of the cloned table */
       
   708 				oCache.nWrapper.style.width = $(s.nTable).outerWidth() + "px";
       
   709 
       
   710 				/* Remove any children the cloned table has */
       
   711 				while (nTable.childNodes.length > 0) {
       
   712 					nTable.removeChild(nTable.childNodes[0]);
       
   713 				}
       
   714 
       
   715 				/* Clone the DataTables footer */
       
   716 				var nTfoot = $('tfoot', s.nTable).clone(true)[0];
       
   717 				nTable.appendChild(nTfoot);
       
   718 
       
   719 				/* Copy the widths across - apparently a clone isn't good enough for this */
       
   720 				$("tfoot:eq(0)>tr th", s.nTable).each(function (i) {
       
   721 					$("tfoot:eq(0)>tr th:eq(" + i + ")", nTable).width($(this).width());
       
   722 				});
       
   723 
       
   724 				$("tfoot:eq(0)>tr td", s.nTable).each(function (i) {
       
   725 					$("tfoot:eq(0)>tr td:eq(" + i + ")", nTable).width($(this).width());
       
   726 				});
       
   727 			},
       
   728 
       
   729 			/*
       
   730 			 * Function: _fnCloneTLeft
       
   731 			 * Purpose:  Clone the left column(s)
       
   732 			 * Returns:  -
       
   733 			 * Inputs:   object:oCache - the cached values for this fixed element
       
   734 			 */
       
   735 			_fnCloneTLeft: function (oCache) {
       
   736 				var s = this.fnGetSettings();
       
   737 				var nTable = oCache.nNode;
       
   738 				var nBody = $('tbody', s.nTable)[0];
       
   739 
       
   740 				/* Remove any children the cloned table has */
       
   741 				while (nTable.childNodes.length > 0) {
       
   742 					nTable.removeChild(nTable.childNodes[0]);
       
   743 				}
       
   744 
       
   745 				/* Is this the most efficient way to do this - it looks horrible... */
       
   746 				nTable.appendChild($("thead", s.nTable).clone(true)[0]);
       
   747 				nTable.appendChild($("tbody", s.nTable).clone(true)[0]);
       
   748 				if (s.bFooter) {
       
   749 					nTable.appendChild($("tfoot", s.nTable).clone(true)[0]);
       
   750 				}
       
   751 
       
   752 				/* Remove unneeded cells */
       
   753 				var sSelector = 'gt(' + (oCache.iCells - 1) + ')';
       
   754 				$('thead tr', nTable).each(function (k) {
       
   755 					$('th:' + sSelector, this).remove();
       
   756 				});
       
   757 
       
   758 				$('tfoot tr', nTable).each(function (k) {
       
   759 					$('th:' + sSelector, this).remove();
       
   760 				});
       
   761 
       
   762 				$('tbody tr', nTable).each(function (k) {
       
   763 					$('td:' + sSelector, this).remove();
       
   764 				});
       
   765 
       
   766 				this.fnEqualiseHeights('thead', nBody.parentNode, nTable);
       
   767 				this.fnEqualiseHeights('tbody', nBody.parentNode, nTable);
       
   768 				this.fnEqualiseHeights('tfoot', nBody.parentNode, nTable);
       
   769 
       
   770 				var iWidth = 0;
       
   771 				for (var i = 0; i < oCache.iCells; i++) {
       
   772 					iWidth += $('thead tr th:eq(' + i + ')', s.nTable).outerWidth();
       
   773 				}
       
   774 				nTable.style.width = iWidth + "px";
       
   775 				oCache.nWrapper.style.width = iWidth + "px";
       
   776 			},
       
   777 
       
   778 			/*
       
   779 			 * Function: _fnCloneTRight
       
   780 			 * Purpose:  Clone the right most column(s)
       
   781 			 * Returns:  -
       
   782 			 * Inputs:   object:oCache - the cached values for this fixed element
       
   783 			 */
       
   784 			_fnCloneTRight: function (oCache) {
       
   785 				var s = this.fnGetSettings();
       
   786 				var nBody = $('tbody', s.nTable)[0];
       
   787 				var nTable = oCache.nNode;
       
   788 				var iCols = $('tbody tr:eq(0) td', s.nTable).length;
       
   789 
       
   790 				/* Remove any children the cloned table has */
       
   791 				while (nTable.childNodes.length > 0) {
       
   792 					nTable.removeChild(nTable.childNodes[0]);
       
   793 				}
       
   794 
       
   795 				/* Is this the most efficient way to do this - it looks horrible... */
       
   796 				nTable.appendChild($("thead", s.nTable).clone(true)[0]);
       
   797 				nTable.appendChild($("tbody", s.nTable).clone(true)[0]);
       
   798 				if (s.bFooter) {
       
   799 					nTable.appendChild($("tfoot", s.nTable).clone(true)[0]);
       
   800 				}
       
   801 				$('thead tr th:lt(' + (iCols - oCache.iCells) + ')', nTable).remove();
       
   802 				$('tfoot tr th:lt(' + (iCols - oCache.iCells) + ')', nTable).remove();
       
   803 
       
   804 				/* Remove unneeded cells */
       
   805 				$('tbody tr', nTable).each(function (k) {
       
   806 					$('td:lt(' + (iCols - oCache.iCells) + ')', this).remove();
       
   807 				});
       
   808 
       
   809 				this.fnEqualiseHeights('thead', nBody.parentNode, nTable);
       
   810 				this.fnEqualiseHeights('tbody', nBody.parentNode, nTable);
       
   811 				this.fnEqualiseHeights('tfoot', nBody.parentNode, nTable);
       
   812 
       
   813 				var iWidth = 0;
       
   814 				for (var i = 0; i < oCache.iCells; i++) {
       
   815 					iWidth += $('thead tr th:eq(' + (iCols - 1 - i) + ')', s.nTable).outerWidth();
       
   816 				}
       
   817 				nTable.style.width = iWidth + "px";
       
   818 				oCache.nWrapper.style.width = iWidth + "px";
       
   819 			},
       
   820 
       
   821 
       
   822 			/**
       
   823 			 * Equalise the heights of the rows in a given table node in a cross browser way. Note that this
       
   824 			 * is more or less lifted as is from FixedColumns
       
   825 			 *  @method  fnEqualiseHeights
       
   826 			 *  @returns void
       
   827 			 *  @param   {string} parent Node type - thead, tbody or tfoot
       
   828 			 *  @param   {element} original Original node to take the heights from
       
   829 			 *  @param   {element} clone Copy the heights to
       
   830 			 *  @private
       
   831 			 */
       
   832 			"fnEqualiseHeights": function (parent, original, clone) {
       
   833 				var that = this;
       
   834 				var originals = $(parent + ' tr', original);
       
   835 				var height;
       
   836 
       
   837 				$(parent + ' tr', clone).each(function (k) {
       
   838 					height = originals.eq(k).css('height');
       
   839 
       
   840 					// This is nasty :-(. IE has a sub-pixel error even when setting
       
   841 					// the height below (the Firefox fix) which causes the fixed column
       
   842 					// to go out of alignment. Need to add a pixel before the assignment
       
   843 					// Can this be feature detected? Not sure how...
       
   844 					if (navigator.appName == 'Microsoft Internet Explorer') {
       
   845 						height = parseInt(height, 10) + 1;
       
   846 					}
       
   847 
       
   848 					$(this).css('height', height);
       
   849 
       
   850 					// For Firefox to work, we need to also set the height of the
       
   851 					// original row, to the value that we read from it! Otherwise there
       
   852 					// is a sub-pixel rounding error
       
   853 					originals.eq(k).css('height', height);
       
   854 				});
       
   855 			}
       
   856 		};
       
   857 
       
   858 
       
   859 		/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
       
   860 		 * Static properties and methods
       
   861 		 *   We use these for speed! This information is common to all instances of FixedHeader, so no
       
   862 		 * point if having them calculated and stored for each different instance.
       
   863 		 */
       
   864 
       
   865 		/*
       
   866 		 * Variable: oWin
       
   867 		 * Purpose:  Store information about the window positioning
       
   868 		 * Scope:    FixedHeader
       
   869 		 */
       
   870 		FixedHeader.oWin = {
       
   871 			"iScrollTop": 0,
       
   872 			"iScrollRight": 0,
       
   873 			"iScrollBottom": 0,
       
   874 			"iScrollLeft": 0,
       
   875 			"iHeight": 0,
       
   876 			"iWidth": 0
       
   877 		};
       
   878 
       
   879 		/*
       
   880 		 * Variable: oDoc
       
   881 		 * Purpose:  Store information about the document size
       
   882 		 * Scope:    FixedHeader
       
   883 		 */
       
   884 		FixedHeader.oDoc = {
       
   885 			"iHeight": 0,
       
   886 			"iWidth": 0
       
   887 		};
       
   888 
       
   889 		/*
       
   890 		 * Variable: afnScroll
       
   891 		 * Purpose:  Array of functions that are to be used for the scrolling components
       
   892 		 * Scope:    FixedHeader
       
   893 		 */
       
   894 		FixedHeader.afnScroll = [];
       
   895 
       
   896 		/*
       
   897 		 * Function: fnMeasure
       
   898 		 * Purpose:  Update the measurements for the window and document
       
   899 		 * Returns:  -
       
   900 		 * Inputs:   -
       
   901 		 */
       
   902 		FixedHeader.fnMeasure = function () {
       
   903 			var
       
   904 				jqWin = $(window),
       
   905 				jqDoc = $(document),
       
   906 				oWin = FixedHeader.oWin,
       
   907 				oDoc = FixedHeader.oDoc;
       
   908 
       
   909 			oDoc.iHeight = jqDoc.height();
       
   910 			oDoc.iWidth = jqDoc.width();
       
   911 
       
   912 			oWin.iHeight = jqWin.height();
       
   913 			oWin.iWidth = jqWin.width();
       
   914 			oWin.iScrollTop = jqWin.scrollTop();
       
   915 			oWin.iScrollLeft = jqWin.scrollLeft();
       
   916 			oWin.iScrollRight = oDoc.iWidth - oWin.iScrollLeft - oWin.iWidth;
       
   917 			oWin.iScrollBottom = oDoc.iHeight - oWin.iScrollTop - oWin.iHeight;
       
   918 		};
       
   919 
       
   920 
       
   921 		FixedHeader.version = "2.1.0";
       
   922 
       
   923 
       
   924 		/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
       
   925 		 * Global processing
       
   926 		 */
       
   927 
       
   928 		/*
       
   929 		 * Just one 'scroll' event handler in FixedHeader, which calls the required components. This is
       
   930 		 * done as an optimisation, to reduce calculation and proagation time
       
   931 		 */
       
   932 		$(window).scroll(function () {
       
   933 			FixedHeader.fnMeasure();
       
   934 
       
   935 			for (var i = 0, iLen = FixedHeader.afnScroll.length; i < iLen; i++) {
       
   936 				FixedHeader.afnScroll[i]();
       
   937 			}
       
   938 		});
       
   939 
       
   940 
       
   941 		$.fn.dataTable.FixedHeader = FixedHeader;
       
   942 		$.fn.DataTable.FixedHeader = FixedHeader;
       
   943 
       
   944 
       
   945 		return FixedHeader;
       
   946 	}; // /factory
       
   947 
       
   948 
       
   949 	factory(jQuery, jQuery.fn.dataTable);
       
   950 
       
   951 
       
   952 })(window, document);
       
   953