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