Blame | Letzte Änderung | Log anzeigen | RSS feed
/*! FixedColumns 3.1.0* ©2010-2014 SpryMedia Ltd - datatables.net/license*//*** @summary FixedColumns* @description Freeze columns in place on a scrolling DataTable* @version 3.1.0* @file dataTables.fixedColumns.js* @author SpryMedia Ltd (www.sprymedia.co.uk)* @contact www.sprymedia.co.uk/contact* @copyright Copyright 2010-2014 SpryMedia Ltd.** This source file is free software, available under the following license:* MIT license - http://datatables.net/license/mit** This source file is distributed in the hope that it will be useful, but* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY* or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.** For details please refer to: http://www.datatables.net*/(function(window, document, undefined) {var factory = function( $, DataTable ) {"use strict";/*** When making use of DataTables' x-axis scrolling feature, you may wish to* fix the left most column in place. This plug-in for DataTables provides* exactly this option (note for non-scrolling tables, please use the* FixedHeader plug-in, which can fix headers, footers and columns). Key* features include:** * Freezes the left or right most columns to the side of the table* * Option to freeze two or more columns* * Full integration with DataTables' scrolling options* * Speed - FixedColumns is fast in its operation** @class* @constructor* @global* @param {object} dt DataTables instance. With DataTables 1.10 this can also* be a jQuery collection, a jQuery selector, DataTables API instance or* settings object.* @param {object} [init={}] Configuration object for FixedColumns. Options are* defined by {@link FixedColumns.defaults}** @requires jQuery 1.7+* @requires DataTables 1.8.0+** @example* var table = $('#example').dataTable( {* "scrollX": "100%"* } );* new $.fn.dataTable.fixedColumns( table );*/var FixedColumns = function ( dt, init ) {var that = this;/* Sanity check - you just know it will happen */if ( ! ( this instanceof FixedColumns ) ) {alert( "FixedColumns warning: FixedColumns must be initialised with the 'new' keyword." );return;}if ( init === undefined || init === true ) {init = {};}// Use the DataTables Hungarian notation mapping method, if it exists to// provide forwards compatibility for camel case variablesvar camelToHungarian = $.fn.dataTable.camelToHungarian;if ( camelToHungarian ) {camelToHungarian( FixedColumns.defaults, FixedColumns.defaults, true );camelToHungarian( FixedColumns.defaults, init );}// v1.10 allows the settings object to be got form a number of sourcesvar dtSettings = new $.fn.dataTable.Api( dt ).settings()[0];/*** Settings object which contains customisable information for FixedColumns instance* @namespace* @extends FixedColumns.defaults* @private*/this.s = {/*** DataTables settings objects* @type object* @default Obtained from DataTables instance*/"dt": dtSettings,/*** Number of columns in the DataTable - stored for quick access* @type int* @default Obtained from DataTables instance*/"iTableColumns": dtSettings.aoColumns.length,/*** Original outer widths of the columns as rendered by DataTables - used to calculate* the FixedColumns grid bounding box* @type array.<int>* @default []*/"aiOuterWidths": [],/*** Original inner widths of the columns as rendered by DataTables - used to apply widths* to the columns* @type array.<int>* @default []*/"aiInnerWidths": []};/*** DOM elements used by the class instance* @namespace* @private**/this.dom = {/*** DataTables scrolling element* @type node* @default null*/"scroller": null,/*** DataTables header table* @type node* @default null*/"header": null,/*** DataTables body table* @type node* @default null*/"body": null,/*** DataTables footer table* @type node* @default null*/"footer": null,/*** Display grid elements* @namespace*/"grid": {/*** Grid wrapper. This is the container element for the 3x3 grid* @type node* @default null*/"wrapper": null,/*** DataTables scrolling element. This element is the DataTables* component in the display grid (making up the main table - i.e.* not the fixed columns).* @type node* @default null*/"dt": null,/*** Left fixed column grid components* @namespace*/"left": {"wrapper": null,"head": null,"body": null,"foot": null},/*** Right fixed column grid components* @namespace*/"right": {"wrapper": null,"head": null,"body": null,"foot": null}},/*** Cloned table nodes* @namespace*/"clone": {/*** Left column cloned table nodes* @namespace*/"left": {/*** Cloned header table* @type node* @default null*/"header": null,/*** Cloned body table* @type node* @default null*/"body": null,/*** Cloned footer table* @type node* @default null*/"footer": null},/*** Right column cloned table nodes* @namespace*/"right": {/*** Cloned header table* @type node* @default null*/"header": null,/*** Cloned body table* @type node* @default null*/"body": null,/*** Cloned footer table* @type node* @default null*/"footer": null}}};if ( dtSettings._oFixedColumns ) {throw 'FixedColumns already initialised on this table';}/* Attach the instance to the DataTables instance so it can be accessed easily */dtSettings._oFixedColumns = this;/* Let's do it */if ( ! dtSettings._bInitComplete ){dtSettings.oApi._fnCallbackReg( dtSettings, 'aoInitComplete', function () {that._fnConstruct( init );}, 'FixedColumns' );}else{this._fnConstruct( init );}};FixedColumns.prototype = /** @lends FixedColumns.prototype */{/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ** Public methods* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *//*** Update the fixed columns - including headers and footers. Note that FixedColumns will* automatically update the display whenever the host DataTable redraws.* @returns {void}* @example* var table = $('#example').dataTable( {* "scrollX": "100%"* } );* var fc = new $.fn.dataTable.fixedColumns( table );** // at some later point when the table has been manipulated....* fc.fnUpdate();*/"fnUpdate": function (){this._fnDraw( true );},/*** Recalculate the resizes of the 3x3 grid that FixedColumns uses for display of the table.* This is useful if you update the width of the table container. Note that FixedColumns will* perform this function automatically when the window.resize event is fired.* @returns {void}* @example* var table = $('#example').dataTable( {* "scrollX": "100%"* } );* var fc = new $.fn.dataTable.fixedColumns( table );** // Resize the table container and then have FixedColumns adjust its layout....* $('#content').width( 1200 );* fc.fnRedrawLayout();*/"fnRedrawLayout": function (){this._fnColCalc();this._fnGridLayout();this.fnUpdate();},/*** Mark a row such that it's height should be recalculated when using 'semiauto' row* height matching. This function will have no effect when 'none' or 'auto' row height* matching is used.* @param {Node} nTr TR element that should have it's height recalculated* @returns {void}* @example* var table = $('#example').dataTable( {* "scrollX": "100%"* } );* var fc = new $.fn.dataTable.fixedColumns( table );** // manipulate the table - mark the row as needing an update then update the table* // this allows the redraw performed by DataTables fnUpdate to recalculate the row* // height* fc.fnRecalculateHeight();* table.fnUpdate( $('#example tbody tr:eq(0)')[0], ["insert date", 1, 2, 3 ... ]);*/"fnRecalculateHeight": function ( nTr ){delete nTr._DTTC_iHeight;nTr.style.height = 'auto';},/*** Set the height of a given row - provides cross browser compatibility* @param {Node} nTarget TR element that should have it's height recalculated* @param {int} iHeight Height in pixels to set* @returns {void}* @example* var table = $('#example').dataTable( {* "scrollX": "100%"* } );* var fc = new $.fn.dataTable.fixedColumns( table );** // You may want to do this after manipulating a row in the fixed column* fc.fnSetRowHeight( $('#example tbody tr:eq(0)')[0], 50 );*/"fnSetRowHeight": function ( nTarget, iHeight ){nTarget.style.height = iHeight+"px";},/*** Get data index information about a row or cell in the table body.* This function is functionally identical to fnGetPosition in DataTables,* taking the same parameter (TH, TD or TR node) and returning exactly the* the same information (data index information). THe difference between* the two is that this method takes into account the fixed columns in the* table, so you can pass in nodes from the master table, or the cloned* tables and get the index position for the data in the main table.* @param {node} node TR, TH or TD element to get the information about* @returns {int} If nNode is given as a TR, then a single index is* returned, or if given as a cell, an array of [row index, column index* (visible), column index (all)] is given.*/"fnGetPosition": function ( node ){var idx;var inst = this.s.dt.oInstance;if ( ! $(node).parents('.DTFC_Cloned').length ){// Not in a cloned tablereturn inst.fnGetPosition( node );}else{// Its in the cloned table, so need to look up positionif ( node.nodeName.toLowerCase() === 'tr' ) {idx = $(node).index();return inst.fnGetPosition( $('tr', this.s.dt.nTBody)[ idx ] );}else{var colIdx = $(node).index();idx = $(node.parentNode).index();var row = inst.fnGetPosition( $('tr', this.s.dt.nTBody)[ idx ] );return [row,colIdx,inst.oApi._fnVisibleToColumnIndex( this.s.dt, colIdx )];}}},/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ** Private methods (they are of course public in JS, but recommended as private)* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *//*** Initialisation for FixedColumns* @param {Object} oInit User settings for initialisation* @returns {void}* @private*/"_fnConstruct": function ( oInit ){var i, iLen, iWidth,that = this;/* Sanity checking */if ( typeof this.s.dt.oInstance.fnVersionCheck != 'function' ||this.s.dt.oInstance.fnVersionCheck( '1.8.0' ) !== true ){alert( "FixedColumns "+FixedColumns.VERSION+" required DataTables 1.8.0 or later. "+"Please upgrade your DataTables installation" );return;}if ( this.s.dt.oScroll.sX === "" ){this.s.dt.oInstance.oApi._fnLog( this.s.dt, 1, "FixedColumns is not needed (no "+"x-scrolling in DataTables enabled), so no action will be taken. Use 'FixedHeader' for "+"column fixing when scrolling is not enabled" );return;}/* Apply the settings from the user / defaults */this.s = $.extend( true, this.s, FixedColumns.defaults, oInit );/* Set up the DOM as we need it and cache nodes */var classes = this.s.dt.oClasses;this.dom.grid.dt = $(this.s.dt.nTable).parents('div.'+classes.sScrollWrapper)[0];this.dom.scroller = $('div.'+classes.sScrollBody, this.dom.grid.dt )[0];/* Set up the DOM that we want for the fixed column layout grid */this._fnColCalc();this._fnGridSetup();/* Event handlers */var mouseController;// When the body is scrolled - scroll the left and right columns$(this.dom.scroller).on( 'mouseover.DTFC touchstart.DTFC', function () {mouseController = 'main';} ).on( 'scroll.DTFC', function (e) {if ( ! mouseController && e.originalEvent ) {mouseController = 'main';}if ( mouseController === 'main' ) {if ( that.s.iLeftColumns > 0 ) {that.dom.grid.left.liner.scrollTop = that.dom.scroller.scrollTop;}if ( that.s.iRightColumns > 0 ) {that.dom.grid.right.liner.scrollTop = that.dom.scroller.scrollTop;}}} );var wheelType = 'onwheel' in document.createElement('div') ?'wheel.DTFC' :'mousewheel.DTFC';if ( that.s.iLeftColumns > 0 ) {// When scrolling the left column, scroll the body and right column$(that.dom.grid.left.liner).on( 'mouseover.DTFC touchstart.DTFC', function () {mouseController = 'left';} ).on( 'scroll.DTFC', function ( e ) {if ( ! mouseController && e.originalEvent ) {mouseController = 'left';}if ( mouseController === 'left' ) {that.dom.scroller.scrollTop = that.dom.grid.left.liner.scrollTop;if ( that.s.iRightColumns > 0 ) {that.dom.grid.right.liner.scrollTop = that.dom.grid.left.liner.scrollTop;}}} ).on( wheelType, function(e) {// Pass horizontal scrolling throughvar xDelta = e.type === 'wheel' ?-e.originalEvent.deltaX :e.originalEvent.wheelDeltaX;that.dom.scroller.scrollLeft -= xDelta;} );}if ( that.s.iRightColumns > 0 ) {// When scrolling the right column, scroll the body and the left column$(that.dom.grid.right.liner).on( 'mouseover.DTFC touchstart.DTFC', function () {mouseController = 'right';} ).on( 'scroll.DTFC', function ( e ) {if ( ! mouseController && e.originalEvent ) {mouseController = 'right';}if ( mouseController === 'right' ) {that.dom.scroller.scrollTop = that.dom.grid.right.liner.scrollTop;if ( that.s.iLeftColumns > 0 ) {that.dom.grid.left.liner.scrollTop = that.dom.grid.right.liner.scrollTop;}}} ).on( wheelType, function(e) {// Pass horizontal scrolling throughvar xDelta = e.type === 'wheel' ?-e.originalEvent.deltaX :e.originalEvent.wheelDeltaX;that.dom.scroller.scrollLeft -= xDelta;} );}$(window).on( 'resize.DTFC', function () {that._fnGridLayout.call( that );} );var bFirstDraw = true;var jqTable = $(this.s.dt.nTable);jqTable.on( 'draw.dt.DTFC', function () {that._fnDraw.call( that, bFirstDraw );bFirstDraw = false;} ).on( 'column-sizing.dt.DTFC', function () {that._fnColCalc();that._fnGridLayout( that );} ).on( 'column-visibility.dt.DTFC', function () {that._fnColCalc();that._fnGridLayout( that );that._fnDraw( true );} ).on( 'destroy.dt.DTFC', function () {jqTable.off( 'column-sizing.dt.DTFC column-visibility.dt.DTFC destroy.dt.DTFC draw.dt.DTFC' );$(that.dom.scroller).off( 'mouseover.DTFC touchstart.DTFC scroll.DTFC' );$(window).off( 'resize.DTFC' );$(that.dom.grid.left.liner).off( 'mouseover.DTFC touchstart.DTFC scroll.DTFC '+wheelType );$(that.dom.grid.left.wrapper).remove();$(that.dom.grid.right.liner).off( 'mouseover.DTFC touchstart.DTFC scroll.DTFC '+wheelType );$(that.dom.grid.right.wrapper).remove();} );/* Get things right to start with - note that due to adjusting the columns, there must be* another redraw of the main table. It doesn't need to be a full redraw however.*/this._fnGridLayout();this.s.dt.oInstance.fnDraw(false);},/*** Calculate the column widths for the grid layout* @returns {void}* @private*/"_fnColCalc": function (){var that = this;var iLeftWidth = 0;var iRightWidth = 0;this.s.aiInnerWidths = [];this.s.aiOuterWidths = [];$.each( this.s.dt.aoColumns, function (i, col) {var th = $(col.nTh);var border;if ( ! th.filter(':visible').length ) {that.s.aiInnerWidths.push( 0 );that.s.aiOuterWidths.push( 0 );}else{// Inner width is used to assign widths to cells// Outer width is used to calculate the containervar iWidth = th.outerWidth();// When working with the left most-cell, need to add on the// table's border to the outerWidth, since we need to take// account of it, but it isn't in any cellif ( that.s.aiOuterWidths.length === 0 ) {border = $(that.s.dt.nTable).css('border-left-width');iWidth += typeof border === 'string' ? 1 : parseInt( border, 10 );}// Likewise with the final column on the rightif ( that.s.aiOuterWidths.length === that.s.dt.aoColumns.length-1 ) {border = $(that.s.dt.nTable).css('border-right-width');iWidth += typeof border === 'string' ? 1 : parseInt( border, 10 );}that.s.aiOuterWidths.push( iWidth );that.s.aiInnerWidths.push( th.width() );if ( i < that.s.iLeftColumns ){iLeftWidth += iWidth;}if ( that.s.iTableColumns-that.s.iRightColumns <= i ){iRightWidth += iWidth;}}} );this.s.iLeftWidth = iLeftWidth;this.s.iRightWidth = iRightWidth;},/*** Set up the DOM for the fixed column. The way the layout works is to create a 1x3 grid* for the left column, the DataTable (for which we just reuse the scrolling element DataTable* puts into the DOM) and the right column. In each of he two fixed column elements there is a* grouping wrapper element and then a head, body and footer wrapper. In each of these we then* place the cloned header, body or footer tables. This effectively gives as 3x3 grid structure.* @returns {void}* @private*/"_fnGridSetup": function (){var that = this;var oOverflow = this._fnDTOverflow();var block;this.dom.body = this.s.dt.nTable;this.dom.header = this.s.dt.nTHead.parentNode;this.dom.header.parentNode.parentNode.style.position = "relative";var nSWrapper =$('<div class="DTFC_ScrollWrapper" style="position:relative; clear:both;">'+'<div class="DTFC_LeftWrapper" style="position:absolute; top:0; left:0;">'+'<div class="DTFC_LeftHeadWrapper" style="position:relative; top:0; left:0; overflow:hidden;"></div>'+'<div class="DTFC_LeftBodyWrapper" style="position:relative; top:0; left:0; overflow:hidden;">'+'<div class="DTFC_LeftBodyLiner" style="position:relative; top:0; left:0; overflow-y:scroll;"></div>'+'</div>'+'<div class="DTFC_LeftFootWrapper" style="position:relative; top:0; left:0; overflow:hidden;"></div>'+'</div>'+'<div class="DTFC_RightWrapper" style="position:absolute; top:0; left:0;">'+'<div class="DTFC_RightHeadWrapper" style="position:relative; top:0; left:0;">'+'<div class="DTFC_RightHeadBlocker DTFC_Blocker" style="position:absolute; top:0; bottom:0;"></div>'+'</div>'+'<div class="DTFC_RightBodyWrapper" style="position:relative; top:0; left:0; overflow:hidden;">'+'<div class="DTFC_RightBodyLiner" style="position:relative; top:0; left:0; overflow-y:scroll;"></div>'+'</div>'+'<div class="DTFC_RightFootWrapper" style="position:relative; top:0; left:0;">'+'<div class="DTFC_RightFootBlocker DTFC_Blocker" style="position:absolute; top:0; bottom:0;"></div>'+'</div>'+'</div>'+'</div>')[0];var nLeft = nSWrapper.childNodes[0];var nRight = nSWrapper.childNodes[1];this.dom.grid.dt.parentNode.insertBefore( nSWrapper, this.dom.grid.dt );nSWrapper.appendChild( this.dom.grid.dt );this.dom.grid.wrapper = nSWrapper;if ( this.s.iLeftColumns > 0 ){this.dom.grid.left.wrapper = nLeft;this.dom.grid.left.head = nLeft.childNodes[0];this.dom.grid.left.body = nLeft.childNodes[1];this.dom.grid.left.liner = $('div.DTFC_LeftBodyLiner', nSWrapper)[0];nSWrapper.appendChild( nLeft );}if ( this.s.iRightColumns > 0 ){this.dom.grid.right.wrapper = nRight;this.dom.grid.right.head = nRight.childNodes[0];this.dom.grid.right.body = nRight.childNodes[1];this.dom.grid.right.liner = $('div.DTFC_RightBodyLiner', nSWrapper)[0];block = $('div.DTFC_RightHeadBlocker', nSWrapper)[0];block.style.width = oOverflow.bar+"px";block.style.right = -oOverflow.bar+"px";this.dom.grid.right.headBlock = block;block = $('div.DTFC_RightFootBlocker', nSWrapper)[0];block.style.width = oOverflow.bar+"px";block.style.right = -oOverflow.bar+"px";this.dom.grid.right.footBlock = block;nSWrapper.appendChild( nRight );}if ( this.s.dt.nTFoot ){this.dom.footer = this.s.dt.nTFoot.parentNode;if ( this.s.iLeftColumns > 0 ){this.dom.grid.left.foot = nLeft.childNodes[2];}if ( this.s.iRightColumns > 0 ){this.dom.grid.right.foot = nRight.childNodes[2];}}},/*** Style and position the grid used for the FixedColumns layout* @returns {void}* @private*/"_fnGridLayout": function (){var oGrid = this.dom.grid;var iWidth = $(oGrid.wrapper).width();var iBodyHeight = $(this.s.dt.nTable.parentNode).outerHeight();var iFullHeight = $(this.s.dt.nTable.parentNode.parentNode).outerHeight();var oOverflow = this._fnDTOverflow();variLeftWidth = this.s.iLeftWidth,iRightWidth = this.s.iRightWidth,iRight;var scrollbarAdjust = function ( node, width ) {if ( ! oOverflow.bar ) {// If there is no scrollbar (Macs) we need to hide the auto scrollbarnode.style.width = (width+20)+"px";node.style.paddingRight = "20px";node.style.boxSizing = "border-box";}else {// Otherwise just overflow by the scrollbarnode.style.width = (width+oOverflow.bar)+"px";}};// When x scrolling - don't paint the fixed columns over the x scrollbarif ( oOverflow.x ){iBodyHeight -= oOverflow.bar;}oGrid.wrapper.style.height = iFullHeight+"px";if ( this.s.iLeftColumns > 0 ){oGrid.left.wrapper.style.width = iLeftWidth+"px";oGrid.left.wrapper.style.height = "1px";oGrid.left.body.style.height = iBodyHeight+"px";if ( oGrid.left.foot ) {oGrid.left.foot.style.top = (oOverflow.x ? oOverflow.bar : 0)+"px"; // shift footer for scrollbar}scrollbarAdjust( oGrid.left.liner, iLeftWidth );oGrid.left.liner.style.height = iBodyHeight+"px";}if ( this.s.iRightColumns > 0 ){iRight = iWidth - iRightWidth;if ( oOverflow.y ){iRight -= oOverflow.bar;}oGrid.right.wrapper.style.width = iRightWidth+"px";oGrid.right.wrapper.style.left = iRight+"px";oGrid.right.wrapper.style.height = "1px";oGrid.right.body.style.height = iBodyHeight+"px";if ( oGrid.right.foot ) {oGrid.right.foot.style.top = (oOverflow.x ? oOverflow.bar : 0)+"px";}scrollbarAdjust( oGrid.right.liner, iRightWidth );oGrid.right.liner.style.height = iBodyHeight+"px";oGrid.right.headBlock.style.display = oOverflow.y ? 'block' : 'none';oGrid.right.footBlock.style.display = oOverflow.y ? 'block' : 'none';}},/*** Get information about the DataTable's scrolling state - specifically if the table is scrolling* on either the x or y axis, and also the scrollbar width.* @returns {object} Information about the DataTables scrolling state with the properties:* 'x', 'y' and 'bar'* @private*/"_fnDTOverflow": function (){var nTable = this.s.dt.nTable;var nTableScrollBody = nTable.parentNode;var out = {"x": false,"y": false,"bar": this.s.dt.oScroll.iBarWidth};if ( nTable.offsetWidth > nTableScrollBody.clientWidth ){out.x = true;}if ( nTable.offsetHeight > nTableScrollBody.clientHeight ){out.y = true;}return out;},/*** Clone and position the fixed columns* @returns {void}* @param {Boolean} bAll Indicate if the header and footer should be updated as well (true)* @private*/"_fnDraw": function ( bAll ){this._fnGridLayout();this._fnCloneLeft( bAll );this._fnCloneRight( bAll );/* Draw callback function */if ( this.s.fnDrawCallback !== null ){this.s.fnDrawCallback.call( this, this.dom.clone.left, this.dom.clone.right );}/* Event triggering */$(this).trigger( 'draw.dtfc', {"leftClone": this.dom.clone.left,"rightClone": this.dom.clone.right} );},/*** Clone the right columns* @returns {void}* @param {Boolean} bAll Indicate if the header and footer should be updated as well (true)* @private*/"_fnCloneRight": function ( bAll ){if ( this.s.iRightColumns <= 0 ) {return;}var that = this,i, jq,aiColumns = [];for ( i=this.s.iTableColumns-this.s.iRightColumns ; i<this.s.iTableColumns ; i++ ) {if ( this.s.dt.aoColumns[i].bVisible ) {aiColumns.push( i );}}this._fnClone( this.dom.clone.right, this.dom.grid.right, aiColumns, bAll );},/*** Clone the left columns* @returns {void}* @param {Boolean} bAll Indicate if the header and footer should be updated as well (true)* @private*/"_fnCloneLeft": function ( bAll ){if ( this.s.iLeftColumns <= 0 ) {return;}var that = this,i, jq,aiColumns = [];for ( i=0 ; i<this.s.iLeftColumns ; i++ ) {if ( this.s.dt.aoColumns[i].bVisible ) {aiColumns.push( i );}}this._fnClone( this.dom.clone.left, this.dom.grid.left, aiColumns, bAll );},/*** Make a copy of the layout object for a header or footer element from DataTables. Note that* this method will clone the nodes in the layout object.* @returns {Array} Copy of the layout array* @param {Object} aoOriginal Layout array from DataTables (aoHeader or aoFooter)* @param {Object} aiColumns Columns to copy* @param {boolean} events Copy cell events or not* @private*/"_fnCopyLayout": function ( aoOriginal, aiColumns, events ){var aReturn = [];var aClones = [];var aCloned = [];for ( var i=0, iLen=aoOriginal.length ; i<iLen ; i++ ){var aRow = [];aRow.nTr = $(aoOriginal[i].nTr).clone(events, false)[0];for ( var j=0, jLen=this.s.iTableColumns ; j<jLen ; j++ ){if ( $.inArray( j, aiColumns ) === -1 ){continue;}var iCloned = $.inArray( aoOriginal[i][j].cell, aCloned );if ( iCloned === -1 ){var nClone = $(aoOriginal[i][j].cell).clone(events, false)[0];aClones.push( nClone );aCloned.push( aoOriginal[i][j].cell );aRow.push( {"cell": nClone,"unique": aoOriginal[i][j].unique} );}else{aRow.push( {"cell": aClones[ iCloned ],"unique": aoOriginal[i][j].unique} );}}aReturn.push( aRow );}return aReturn;},/*** Clone the DataTable nodes and place them in the DOM (sized correctly)* @returns {void}* @param {Object} oClone Object containing the header, footer and body cloned DOM elements* @param {Object} oGrid Grid object containing the display grid elements for the cloned* column (left or right)* @param {Array} aiColumns Column indexes which should be operated on from the DataTable* @param {Boolean} bAll Indicate if the header and footer should be updated as well (true)* @private*/"_fnClone": function ( oClone, oGrid, aiColumns, bAll ){var that = this,i, iLen, j, jLen, jq, nTarget, iColumn, nClone, iIndex, aoCloneLayout,jqCloneThead, aoFixedHeader,dt = this.s.dt;/** Header*/if ( bAll ){$(oClone.header).remove();oClone.header = $(this.dom.header).clone(true, false)[0];oClone.header.className += " DTFC_Cloned";oClone.header.style.width = "100%";oGrid.head.appendChild( oClone.header );/* Copy the DataTables layout cache for the header for our floating column */aoCloneLayout = this._fnCopyLayout( dt.aoHeader, aiColumns, true );jqCloneThead = $('>thead', oClone.header);jqCloneThead.empty();/* Add the created cloned TR elements to the table */for ( i=0, iLen=aoCloneLayout.length ; i<iLen ; i++ ){jqCloneThead[0].appendChild( aoCloneLayout[i].nTr );}/* Use the handy _fnDrawHead function in DataTables to do the rowspan/colspan* calculations for us*/dt.oApi._fnDrawHead( dt, aoCloneLayout, true );}else{/* To ensure that we copy cell classes exactly, regardless of colspan, multiple rows* etc, we make a copy of the header from the DataTable again, but don't insert the* cloned cells, just copy the classes across. To get the matching layout for the* fixed component, we use the DataTables _fnDetectHeader method, allowing 1:1 mapping*/aoCloneLayout = this._fnCopyLayout( dt.aoHeader, aiColumns, false );aoFixedHeader=[];dt.oApi._fnDetectHeader( aoFixedHeader, $('>thead', oClone.header)[0] );for ( i=0, iLen=aoCloneLayout.length ; i<iLen ; i++ ){for ( j=0, jLen=aoCloneLayout[i].length ; j<jLen ; j++ ){aoFixedHeader[i][j].cell.className = aoCloneLayout[i][j].cell.className;// If jQuery UI theming is used we need to copy those elements as well$('span.DataTables_sort_icon', aoFixedHeader[i][j].cell).each( function () {this.className = $('span.DataTables_sort_icon', aoCloneLayout[i][j].cell)[0].className;} );}}}this._fnEqualiseHeights( 'thead', this.dom.header, oClone.header );/** Body*/if ( this.s.sHeightMatch == 'auto' ){/* Remove any heights which have been applied already and let the browser figure it out */$('>tbody>tr', that.dom.body).css('height', 'auto');}if ( oClone.body !== null ){$(oClone.body).remove();oClone.body = null;}oClone.body = $(this.dom.body).clone(true)[0];oClone.body.className += " DTFC_Cloned";oClone.body.style.paddingBottom = dt.oScroll.iBarWidth+"px";oClone.body.style.marginBottom = (dt.oScroll.iBarWidth*2)+"px"; /* For IE */if ( oClone.body.getAttribute('id') !== null ){oClone.body.removeAttribute('id');}$('>thead>tr', oClone.body).empty();$('>tfoot', oClone.body).remove();var nBody = $('tbody', oClone.body)[0];$(nBody).empty();if ( dt.aiDisplay.length > 0 ){/* Copy the DataTables' header elements to force the column width in exactly the* same way that DataTables does it - have the header element, apply the width and* colapse it down*/var nInnerThead = $('>thead>tr', oClone.body)[0];for ( iIndex=0 ; iIndex<aiColumns.length ; iIndex++ ){iColumn = aiColumns[iIndex];nClone = $(dt.aoColumns[iColumn].nTh).clone(true)[0];nClone.innerHTML = "";var oStyle = nClone.style;oStyle.paddingTop = "0";oStyle.paddingBottom = "0";oStyle.borderTopWidth = "0";oStyle.borderBottomWidth = "0";oStyle.height = 0;oStyle.width = that.s.aiInnerWidths[iColumn]+"px";nInnerThead.appendChild( nClone );}/* Add in the tbody elements, cloning form the master table */$('>tbody>tr', that.dom.body).each( function (z) {var n = this.cloneNode(false);n.removeAttribute('id');var i = that.s.dt.oFeatures.bServerSide===false ?that.s.dt.aiDisplay[ that.s.dt._iDisplayStart+z ] : z;var aTds = that.s.dt.aoData[ i ].anCells || $(this).children('td, th');for ( iIndex=0 ; iIndex<aiColumns.length ; iIndex++ ){iColumn = aiColumns[iIndex];if ( aTds.length > 0 ){nClone = $( aTds[iColumn] ).clone(true, true)[0];n.appendChild( nClone );}}nBody.appendChild( n );} );}else{$('>tbody>tr', that.dom.body).each( function (z) {nClone = this.cloneNode(true);nClone.className += ' DTFC_NoData';$('td', nClone).html('');nBody.appendChild( nClone );} );}oClone.body.style.width = "100%";oClone.body.style.margin = "0";oClone.body.style.padding = "0";// Interop with Scroller - need to use a height forcing element in the// scrolling area in the same way that Scroller does in the body scroll.if ( dt.oScroller !== undefined ){var scrollerForcer = dt.oScroller.dom.force;if ( ! oGrid.forcer ) {oGrid.forcer = scrollerForcer.cloneNode( true );oGrid.liner.appendChild( oGrid.forcer );}else {oGrid.forcer.style.height = scrollerForcer.style.height;}}oGrid.liner.appendChild( oClone.body );this._fnEqualiseHeights( 'tbody', that.dom.body, oClone.body );/** Footer*/if ( dt.nTFoot !== null ){if ( bAll ){if ( oClone.footer !== null ){oClone.footer.parentNode.removeChild( oClone.footer );}oClone.footer = $(this.dom.footer).clone(true, true)[0];oClone.footer.className += " DTFC_Cloned";oClone.footer.style.width = "100%";oGrid.foot.appendChild( oClone.footer );/* Copy the footer just like we do for the header */aoCloneLayout = this._fnCopyLayout( dt.aoFooter, aiColumns, true );var jqCloneTfoot = $('>tfoot', oClone.footer);jqCloneTfoot.empty();for ( i=0, iLen=aoCloneLayout.length ; i<iLen ; i++ ){jqCloneTfoot[0].appendChild( aoCloneLayout[i].nTr );}dt.oApi._fnDrawHead( dt, aoCloneLayout, true );}else{aoCloneLayout = this._fnCopyLayout( dt.aoFooter, aiColumns, false );var aoCurrFooter=[];dt.oApi._fnDetectHeader( aoCurrFooter, $('>tfoot', oClone.footer)[0] );for ( i=0, iLen=aoCloneLayout.length ; i<iLen ; i++ ){for ( j=0, jLen=aoCloneLayout[i].length ; j<jLen ; j++ ){aoCurrFooter[i][j].cell.className = aoCloneLayout[i][j].cell.className;}}}this._fnEqualiseHeights( 'tfoot', this.dom.footer, oClone.footer );}/* Equalise the column widths between the header footer and body - body get's priority */var anUnique = dt.oApi._fnGetUniqueThs( dt, $('>thead', oClone.header)[0] );$(anUnique).each( function (i) {iColumn = aiColumns[i];this.style.width = that.s.aiInnerWidths[iColumn]+"px";} );if ( that.s.dt.nTFoot !== null ){anUnique = dt.oApi._fnGetUniqueThs( dt, $('>tfoot', oClone.footer)[0] );$(anUnique).each( function (i) {iColumn = aiColumns[i];this.style.width = that.s.aiInnerWidths[iColumn]+"px";} );}},/*** From a given table node (THEAD etc), get a list of TR direct child elements* @param {Node} nIn Table element to search for TR elements (THEAD, TBODY or TFOOT element)* @returns {Array} List of TR elements found* @private*/"_fnGetTrNodes": function ( nIn ){var aOut = [];for ( var i=0, iLen=nIn.childNodes.length ; i<iLen ; i++ ){if ( nIn.childNodes[i].nodeName.toUpperCase() == "TR" ){aOut.push( nIn.childNodes[i] );}}return aOut;},/*** Equalise the heights of the rows in a given table node in a cross browser way* @returns {void}* @param {String} nodeName Node type - thead, tbody or tfoot* @param {Node} original Original node to take the heights from* @param {Node} clone Copy the heights to* @private*/"_fnEqualiseHeights": function ( nodeName, original, clone ){if ( this.s.sHeightMatch == 'none' && nodeName !== 'thead' && nodeName !== 'tfoot' ){return;}var that = this,i, iLen, iHeight, iHeight2, iHeightOriginal, iHeightClone,rootOriginal = original.getElementsByTagName(nodeName)[0],rootClone = clone.getElementsByTagName(nodeName)[0],jqBoxHack = $('>'+nodeName+'>tr:eq(0)', original).children(':first'),iBoxHack = jqBoxHack.outerHeight() - jqBoxHack.height(),anOriginal = this._fnGetTrNodes( rootOriginal ),anClone = this._fnGetTrNodes( rootClone ),heights = [];for ( i=0, iLen=anClone.length ; i<iLen ; i++ ){iHeightOriginal = anOriginal[i].offsetHeight;iHeightClone = anClone[i].offsetHeight;iHeight = iHeightClone > iHeightOriginal ? iHeightClone : iHeightOriginal;if ( this.s.sHeightMatch == 'semiauto' ){anOriginal[i]._DTTC_iHeight = iHeight;}heights.push( iHeight );}for ( i=0, iLen=anClone.length ; i<iLen ; i++ ){anClone[i].style.height = heights[i]+"px";anOriginal[i].style.height = heights[i]+"px";}}};/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ** Statics* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *//*** FixedColumns default settings for initialisation* @name FixedColumns.defaults* @namespace* @static*/FixedColumns.defaults = /** @lends FixedColumns.defaults */{/*** Number of left hand columns to fix in position* @type int* @default 1* @static* @example* var = $('#example').dataTable( {* "scrollX": "100%"* } );* new $.fn.dataTable.fixedColumns( table, {* "leftColumns": 2* } );*/"iLeftColumns": 1,/*** Number of right hand columns to fix in position* @type int* @default 0* @static* @example* var table = $('#example').dataTable( {* "scrollX": "100%"* } );* new $.fn.dataTable.fixedColumns( table, {* "rightColumns": 1* } );*/"iRightColumns": 0,/*** Draw callback function which is called when FixedColumns has redrawn the fixed assets* @type function(object, object):void* @default null* @static* @example* var table = $('#example').dataTable( {* "scrollX": "100%"* } );* new $.fn.dataTable.fixedColumns( table, {* "drawCallback": function () {* alert( "FixedColumns redraw" );* }* } );*/"fnDrawCallback": null,/*** Height matching algorthim to use. This can be "none" which will result in no height* matching being applied by FixedColumns (height matching could be forced by CSS in this* case), "semiauto" whereby the height calculation will be performed once, and the result* cached to be used again (fnRecalculateHeight can be used to force recalculation), or* "auto" when height matching is performed on every draw (slowest but must accurate)* @type string* @default semiauto* @static* @example* var table = $('#example').dataTable( {* "scrollX": "100%"* } );* new $.fn.dataTable.fixedColumns( table, {* "heightMatch": "auto"* } );*/"sHeightMatch": "semiauto"};/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ** Constants* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *//*** FixedColumns version* @name FixedColumns.version* @type String* @default See code* @static*/FixedColumns.version = "3.1.0";/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ** DataTables API integration* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */DataTable.Api.register( 'fixedColumns()', function () {return this;} );DataTable.Api.register( 'fixedColumns().update()', function () {return this.iterator( 'table', function ( ctx ) {if ( ctx._oFixedColumns ) {ctx._oFixedColumns.fnUpdate();}} );} );DataTable.Api.register( 'fixedColumns().relayout()', function () {return this.iterator( 'table', function ( ctx ) {if ( ctx._oFixedColumns ) {ctx._oFixedColumns.fnRedrawLayout();}} );} );DataTable.Api.register( 'rows().recalcHeight()', function () {return this.iterator( 'row', function ( ctx, idx ) {if ( ctx._oFixedColumns ) {ctx._oFixedColumns.fnRecalculateHeight( this.row(idx).node() );}} );} );DataTable.Api.register( 'fixedColumns().rowIndex()', function ( row ) {row = $(row);return row.parents('.DTFC_Cloned').length ?this.rows( { page: 'current' } ).indexes()[ row.index() ] :this.row( row ).index();} );DataTable.Api.register( 'fixedColumns().cellIndex()', function ( cell ) {cell = $(cell);if ( cell.parents('.DTFC_Cloned').length ) {var rowClonedIdx = cell.parent().index();var rowIdx = this.rows( { page: 'current' } ).indexes()[ rowClonedIdx ];var columnIdx;if ( cell.parents('.DTFC_LeftWrapper').length ) {columnIdx = cell.index();}else {var columns = this.columns().flatten().length;columnIdx = columns - this.context[0]._oFixedColumns.s.iRightColumns + cell.index();}return {row: rowIdx,column: this.column.index( 'toData', columnIdx ),columnVisible: columnIdx};}else {return this.cell( cell ).index();}} );/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ** Initialisation* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */// Attach a listener to the document which listens for DataTables initialisation// events so we can automatically initialise$(document).on( 'init.dt.fixedColumns', function (e, settings) {if ( e.namespace !== 'dt' ) {return;}var init = settings.oInit.fixedColumns;var defaults = DataTable.defaults.fixedColumns;if ( init || defaults ) {var opts = $.extend( {}, init, defaults );if ( init !== false ) {new FixedColumns( settings, opts );}}} );// Make FixedColumns accessible from the DataTables instance$.fn.dataTable.FixedColumns = FixedColumns;$.fn.DataTable.FixedColumns = FixedColumns;return FixedColumns;}; // /factory// Define as an AMD module if possibleif ( typeof define === 'function' && define.amd ) {define( ['jquery', 'datatables'], factory );}else if ( typeof exports === 'object' ) {// Node/CommonJSfactory( require('jquery'), require('datatables') );}else if ( jQuery && !jQuery.fn.dataTable.FixedColumns ) {// Otherwise simply initialise as normal, stopping multiple evaluationfactory( jQuery, jQuery.fn.dataTable );}})(window, document);