2 * Convert a CSS unit width to pixels (e.g. 2em)
3 * @param {string} sWidth width to be converted
4 * @param {node} nParent parent to get the with for (required for relative widths) - optional
5 * @returns {int} iWidth width in pixels
6 * @memberof DataTable#oApi
8 function _fnConvertToWidth ( sWidth, nParent )
10 if ( !sWidth || sWidth === null || sWidth === '' )
17 nParent = document.body;
21 var nTmp = document.createElement( "div" );
22 nTmp.style.width = _fnStringToCss( sWidth );
24 nParent.appendChild( nTmp );
25 iWidth = nTmp.offsetWidth;
26 nParent.removeChild( nTmp );
33 * Calculate the width of columns for the table
34 * @param {object} oSettings dataTables settings object
35 * @memberof DataTable#oApi
37 function _fnCalculateColumnWidths ( oSettings )
39 var iTableWidth = oSettings.nTable.offsetWidth;
42 var iVisibleColumns = 0;
43 var iColums = oSettings.aoColumns.length;
44 var i, iIndex, iCorrector, iWidth;
45 var oHeaders = $('th', oSettings.nTHead);
46 var widthAttr = oSettings.nTable.getAttribute('width');
47 var nWrapper = oSettings.nTable.parentNode;
49 /* Convert any user input sizes into pixel sizes */
50 for ( i=0 ; i<iColums ; i++ )
52 if ( oSettings.aoColumns[i].bVisible )
56 if ( oSettings.aoColumns[i].sWidth !== null )
58 iTmpWidth = _fnConvertToWidth( oSettings.aoColumns[i].sWidthOrig,
60 if ( iTmpWidth !== null )
62 oSettings.aoColumns[i].sWidth = _fnStringToCss( iTmpWidth );
70 /* If the number of columns in the DOM equals the number that we have to process in
71 * DataTables, then we can use the offsets that are created by the web-browser. No custom
72 * sizes can be set in order for this to happen, nor scrolling used
74 if ( iColums == oHeaders.length && iUserInputs === 0 && iVisibleColumns == iColums &&
75 oSettings.oScroll.sX === "" && oSettings.oScroll.sY === "" )
77 for ( i=0 ; i<oSettings.aoColumns.length ; i++ )
79 iTmpWidth = $(oHeaders[i]).width();
80 if ( iTmpWidth !== null )
82 oSettings.aoColumns[i].sWidth = _fnStringToCss( iTmpWidth );
88 /* Otherwise we are going to have to do some calculations to get the width of each column.
89 * Construct a 1 row table with the widest node in the data, and any user defined widths,
90 * then insert it into the DOM and allow the browser to do all the hard work of
91 * calculating table widths.
94 nCalcTmp = oSettings.nTable.cloneNode( false ),
95 nTheadClone = oSettings.nTHead.cloneNode(true),
96 nBody = document.createElement( 'tbody' ),
97 nTr = document.createElement( 'tr' ),
100 nCalcTmp.removeAttribute( "id" );
101 nCalcTmp.appendChild( nTheadClone );
102 if ( oSettings.nTFoot !== null )
104 nCalcTmp.appendChild( oSettings.nTFoot.cloneNode(true) );
105 _fnApplyToChildren( function(n) {
107 }, nCalcTmp.getElementsByTagName('tr') );
110 nCalcTmp.appendChild( nBody );
111 nBody.appendChild( nTr );
113 /* Remove any sizing that was previously applied by the styles */
114 var jqColSizing = $('thead th', nCalcTmp);
115 if ( jqColSizing.length === 0 )
117 jqColSizing = $('tbody tr:eq(0)>td', nCalcTmp);
120 /* Apply custom sizing to the cloned header */
121 var nThs = _fnGetUniqueThs( oSettings, nTheadClone );
123 for ( i=0 ; i<iColums ; i++ )
125 var oColumn = oSettings.aoColumns[i];
126 if ( oColumn.bVisible && oColumn.sWidthOrig !== null && oColumn.sWidthOrig !== "" )
128 nThs[i-iCorrector].style.width = _fnStringToCss( oColumn.sWidthOrig );
130 else if ( oColumn.bVisible )
132 nThs[i-iCorrector].style.width = "";
140 /* Find the biggest td for each column and put it into the table */
141 for ( i=0 ; i<iColums ; i++ )
143 if ( oSettings.aoColumns[i].bVisible )
145 var nTd = _fnGetWidestNode( oSettings, i );
148 nTd = nTd.cloneNode(true);
149 if ( oSettings.aoColumns[i].sContentPadding !== "" )
151 nTd.innerHTML += oSettings.aoColumns[i].sContentPadding;
153 nTr.appendChild( nTd );
158 /* Build the table and 'display' it */
159 nWrapper.appendChild( nCalcTmp );
161 /* When scrolling (X or Y) we want to set the width of the table as appropriate. However,
162 * when not scrolling leave the table width as it is. This results in slightly different,
163 * but I think correct behaviour
165 if ( oSettings.oScroll.sX !== "" && oSettings.oScroll.sXInner !== "" )
167 nCalcTmp.style.width = _fnStringToCss(oSettings.oScroll.sXInner);
169 else if ( oSettings.oScroll.sX !== "" )
171 nCalcTmp.style.width = "";
172 if ( $(nCalcTmp).width() < nWrapper.offsetWidth )
174 nCalcTmp.style.width = _fnStringToCss( nWrapper.offsetWidth );
177 else if ( oSettings.oScroll.sY !== "" )
179 nCalcTmp.style.width = _fnStringToCss( nWrapper.offsetWidth );
181 else if ( widthAttr )
183 nCalcTmp.style.width = _fnStringToCss( widthAttr );
185 nCalcTmp.style.visibility = "hidden";
187 /* Scrolling considerations */
188 _fnScrollingWidthAdjust( oSettings, nCalcTmp );
190 /* Read the width's calculated by the browser and store them for use by the caller. We
191 * first of all try to use the elements in the body, but it is possible that there are
192 * no elements there, under which circumstances we use the header elements
194 var oNodes = $("tbody tr:eq(0)", nCalcTmp).children();
195 if ( oNodes.length === 0 )
197 oNodes = _fnGetUniqueThs( oSettings, $('thead', nCalcTmp)[0] );
200 /* Browsers need a bit of a hand when a width is assigned to any columns when
201 * x-scrolling as they tend to collapse the table to the min-width, even if
202 * we sent the column widths. So we need to keep track of what the table width
203 * should be by summing the user given values, and the automatic values
205 if ( oSettings.oScroll.sX !== "" )
209 for ( i=0 ; i<oSettings.aoColumns.length ; i++ )
211 if ( oSettings.aoColumns[i].bVisible )
213 if ( oSettings.aoColumns[i].sWidthOrig === null )
215 iTotal += $(oNodes[iCorrector]).outerWidth();
219 iTotal += parseInt(oSettings.aoColumns[i].sWidth.replace('px',''), 10) +
220 ($(oNodes[iCorrector]).outerWidth() - $(oNodes[iCorrector]).width());
226 nCalcTmp.style.width = _fnStringToCss( iTotal );
227 oSettings.nTable.style.width = _fnStringToCss( iTotal );
231 for ( i=0 ; i<oSettings.aoColumns.length ; i++ )
233 if ( oSettings.aoColumns[i].bVisible )
235 iWidth = $(oNodes[iCorrector]).width();
236 if ( iWidth !== null && iWidth > 0 )
238 oSettings.aoColumns[i].sWidth = _fnStringToCss( iWidth );
244 var cssWidth = $(nCalcTmp).css('width');
245 oSettings.nTable.style.width = (cssWidth.indexOf('%') !== -1) ?
246 cssWidth : _fnStringToCss( $(nCalcTmp).outerWidth() );
247 nCalcTmp.parentNode.removeChild( nCalcTmp );
252 oSettings.nTable.style.width = _fnStringToCss( widthAttr );
258 * Adjust a table's width to take account of scrolling
259 * @param {object} oSettings dataTables settings object
260 * @param {node} n table node
261 * @memberof DataTable#oApi
263 function _fnScrollingWidthAdjust ( oSettings, n )
265 if ( oSettings.oScroll.sX === "" && oSettings.oScroll.sY !== "" )
267 /* When y-scrolling only, we want to remove the width of the scroll bar so the table
268 * + scroll bar will fit into the area avaialble.
270 var iOrigWidth = $(n).width();
271 n.style.width = _fnStringToCss( $(n).outerWidth()-oSettings.oScroll.iBarWidth );
273 else if ( oSettings.oScroll.sX !== "" )
275 /* When x-scrolling both ways, fix the table at it's current size, without adjusting */
276 n.style.width = _fnStringToCss( $(n).outerWidth() );
282 * Get the widest node
283 * @param {object} oSettings dataTables settings object
284 * @param {int} iCol column of interest
285 * @returns {node} widest table node
286 * @memberof DataTable#oApi
288 function _fnGetWidestNode( oSettings, iCol )
290 var iMaxIndex = _fnGetMaxLenString( oSettings, iCol );
296 if ( oSettings.aoData[iMaxIndex].nTr === null )
298 var n = document.createElement('td');
299 n.innerHTML = _fnGetCellData( oSettings, iMaxIndex, iCol, '' );
302 return _fnGetTdNodes(oSettings, iMaxIndex)[iCol];
307 * Get the maximum strlen for each data column
308 * @param {object} oSettings dataTables settings object
309 * @param {int} iCol column of interest
310 * @returns {string} max string length for each column
311 * @memberof DataTable#oApi
313 function _fnGetMaxLenString( oSettings, iCol )
318 for ( var i=0 ; i<oSettings.aoData.length ; i++ )
320 var s = _fnGetCellData( oSettings, i, iCol, 'display' )+"";
321 s = s.replace( /<.*?>/g, "" );
322 if ( s.length > iMax )
334 * Append a CSS unit (only if required) to a string
335 * @param {array} aArray1 first array
336 * @param {array} aArray2 second array
337 * @returns {int} 0 if match, 1 if length is different, 2 if no match
338 * @memberof DataTable#oApi
340 function _fnStringToCss( s )
347 if ( typeof s == 'number' )
356 /* Check if the last character is not 0-9 */
357 var c = s.charCodeAt( s.length-1 );
358 if (c < 0x30 || c > 0x39)
367 * Get the width of a scroll bar in this browser being used
368 * @returns {int} width in pixels
369 * @memberof DataTable#oApi
371 function _fnScrollBarWidth ()
373 var inner = document.createElement('p');
374 var style = inner.style;
375 style.width = "100%";
376 style.height = "200px";
377 style.padding = "0px";
379 var outer = document.createElement('div');
381 style.position = "absolute";
384 style.visibility = "hidden";
385 style.width = "200px";
386 style.height = "150px";
387 style.padding = "0px";
388 style.overflow = "hidden";
389 outer.appendChild(inner);
391 document.body.appendChild(outer);
392 var w1 = inner.offsetWidth;
393 outer.style.overflow = 'scroll';
394 var w2 = inner.offsetWidth;
397 w2 = outer.clientWidth;
400 document.body.removeChild(outer);