1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336
|
var __re_html_remove = /<.*?>/g;
/**
* Calculate the width of columns for the table
* @param {object} oSettings dataTables settings object
* @memberof DataTable#oApi
*/
function _fnCalculateColumnWidths ( oSettings )
{
var
table = oSettings.nTable,
columns = oSettings.aoColumns,
scroll = oSettings.oScroll,
scrollY = scroll.sY,
scrollX = scroll.sX,
scrollXInner = scroll.sXInner,
columnCount = columns.length,
visibleColumns = _fnGetColumns( oSettings, 'bVisible' ),
headerCells = $('th', oSettings.nTHead),
tableWidthAttr = table.getAttribute('width'), // from DOM element
tableContainer = table.parentNode,
userInputs = false,
i, column, columnIdx, width, outerWidth,
browser = oSettings.oBrowser,
ie67 = browser.bScrollOversize;
var styleWidth = table.style.width;
if ( styleWidth && styleWidth.indexOf('%') !== -1 ) {
tableWidthAttr = styleWidth;
}
/* Convert any user input sizes into pixel sizes */
for ( i=0 ; i<visibleColumns.length ; i++ ) {
column = columns[ visibleColumns[i] ];
if ( column.sWidth !== null ) {
column.sWidth = _fnConvertToWidth( column.sWidthOrig, tableContainer );
userInputs = true;
}
}
/* If the number of columns in the DOM equals the number that we have to
* process in DataTables, then we can use the offsets that are created by
* the web- browser. No custom sizes can be set in order for this to happen,
* nor scrolling used
*/
if ( ie67 || ! userInputs && ! scrollX && ! scrollY &&
columnCount == _fnVisbleColumns( oSettings ) &&
columnCount == headerCells.length
) {
for ( i=0 ; i<columnCount ; i++ ) {
var colIdx = _fnVisibleToColumnIndex( oSettings, i );
if ( colIdx !== null ) {
columns[ colIdx ].sWidth = _fnStringToCss( headerCells.eq(i).width() );
}
}
}
else
{
// Otherwise construct a single row, worst case, table with the widest
// node in the data, assign any user defined widths, then insert it into
// the DOM and allow the browser to do all the hard work of calculating
// table widths
var tmpTable = $(table).clone() // don't use cloneNode - IE8 will remove events on the main table
.css( 'visibility', 'hidden' )
.removeAttr( 'id' );
// Clean up the table body
tmpTable.find('tbody tr').remove();
var tr = $('<tr/>').appendTo( tmpTable.find('tbody') );
// Clone the table header and footer - we can't use the header / footer
// from the cloned table, since if scrolling is active, the table's
// real header and footer are contained in different table tags
tmpTable.find('thead, tfoot').remove();
tmpTable
.append( $(oSettings.nTHead).clone() )
.append( $(oSettings.nTFoot).clone() );
// Remove any assigned widths from the footer (from scrolling)
tmpTable.find('tfoot th, tfoot td').css('width', '');
// Apply custom sizing to the cloned header
headerCells = _fnGetUniqueThs( oSettings, tmpTable.find('thead')[0] );
for ( i=0 ; i<visibleColumns.length ; i++ ) {
column = columns[ visibleColumns[i] ];
headerCells[i].style.width = column.sWidthOrig !== null && column.sWidthOrig !== '' ?
_fnStringToCss( column.sWidthOrig ) :
'';
// For scrollX we need to force the column width otherwise the
// browser will collapse it. If this width is smaller than the
// width the column requires, then it will have no effect
if ( column.sWidthOrig && scrollX ) {
$( headerCells[i] ).append( $('<div/>').css( {
width: column.sWidthOrig,
margin: 0,
padding: 0,
border: 0,
height: 1
} ) );
}
}
// Find the widest cell for each column and put it into the table
if ( oSettings.aoData.length ) {
for ( i=0 ; i<visibleColumns.length ; i++ ) {
columnIdx = visibleColumns[i];
column = columns[ columnIdx ];
$( _fnGetWidestNode( oSettings, columnIdx ) )
.clone( false )
.append( column.sContentPadding )
.appendTo( tr );
}
}
// Tidy the temporary table - remove name attributes so there aren't
// duplicated in the dom (radio elements for example)
$('[name]', tmpTable).removeAttr('name');
// Table has been built, attach to the document so we can work with it.
// A holding element is used, positioned at the top of the container
// with minimal height, so it has no effect on if the container scrolls
// or not. Otherwise it might trigger scrolling when it actually isn't
// needed
var holder = $('<div/>').css( scrollX || scrollY ?
{
position: 'absolute',
top: 0,
left: 0,
height: 1,
right: 0,
overflow: 'hidden'
} :
{}
)
.append( tmpTable )
.appendTo( tableContainer );
// When scrolling (X or Y) we want to set the width of the table as
// appropriate. However, when not scrolling leave the table width as it
// is. This results in slightly different, but I think correct behaviour
if ( scrollX && scrollXInner ) {
tmpTable.width( scrollXInner );
}
else if ( scrollX ) {
tmpTable.css( 'width', 'auto' );
tmpTable.removeAttr('width');
// If there is no width attribute or style, then allow the table to
// collapse
if ( tmpTable.width() < tableContainer.clientWidth && tableWidthAttr ) {
tmpTable.width( tableContainer.clientWidth );
}
}
else if ( scrollY ) {
tmpTable.width( tableContainer.clientWidth );
}
else if ( tableWidthAttr ) {
tmpTable.width( tableWidthAttr );
}
// Get the width of each column in the constructed table - we need to
// know the inner width (so it can be assigned to the other table's
// cells) and the outer width so we can calculate the full width of the
// table. This is safe since DataTables requires a unique cell for each
// column, but if ever a header can span multiple columns, this will
// need to be modified.
var total = 0;
for ( i=0 ; i<visibleColumns.length ; i++ ) {
var cell = $(headerCells[i]);
var border = cell.outerWidth() - cell.width();
// Use getBounding... where possible (not IE8-) because it can give
// sub-pixel accuracy, which we then want to round up!
var bounding = browser.bBounding ?
Math.ceil( headerCells[i].getBoundingClientRect().width ) :
cell.outerWidth();
// Total is tracked to remove any sub-pixel errors as the outerWidth
// of the table might not equal the total given here (IE!).
total += bounding;
// Width for each column to use
columns[ visibleColumns[i] ].sWidth = _fnStringToCss( bounding - border );
}
table.style.width = _fnStringToCss( total );
// Finished with the table - ditch it
holder.remove();
}
// If there is a width attr, we want to attach an event listener which
// allows the table sizing to automatically adjust when the window is
// resized. Use the width attr rather than CSS, since we can't know if the
// CSS is a relative value or absolute - DOM read is always px.
if ( tableWidthAttr ) {
table.style.width = _fnStringToCss( tableWidthAttr );
}
if ( (tableWidthAttr || scrollX) && ! oSettings._reszEvt ) {
var bindResize = function () {
$(window).on('resize.DT-'+oSettings.sInstance, _fnThrottle( function () {
_fnAdjustColumnSizing( oSettings );
} ) );
};
// IE6/7 will crash if we bind a resize event handler on page load.
// To be removed in 1.11 which drops IE6/7 support
if ( ie67 ) {
setTimeout( bindResize, 1000 );
}
else {
bindResize();
}
oSettings._reszEvt = true;
}
}
/**
* Throttle the calls to a function. Arguments and context are maintained for
* the throttled function
* @param {function} fn Function to be called
* @param {int} [freq=200] call frequency in mS
* @returns {function} wrapped function
* @memberof DataTable#oApi
*/
var _fnThrottle = DataTable.util.throttle;
/**
* Convert a CSS unit width to pixels (e.g. 2em)
* @param {string} width width to be converted
* @param {node} parent parent to get the with for (required for relative widths) - optional
* @returns {int} width in pixels
* @memberof DataTable#oApi
*/
function _fnConvertToWidth ( width, parent )
{
if ( ! width ) {
return 0;
}
var n = $('<div/>')
.css( 'width', _fnStringToCss( width ) )
.appendTo( parent || document.body );
var val = n[0].offsetWidth;
n.remove();
return val;
}
/**
* Get the widest node
* @param {object} settings dataTables settings object
* @param {int} colIdx column of interest
* @returns {node} widest table node
* @memberof DataTable#oApi
*/
function _fnGetWidestNode( settings, colIdx )
{
var idx = _fnGetMaxLenString( settings, colIdx );
if ( idx < 0 ) {
return null;
}
var data = settings.aoData[ idx ];
return ! data.nTr ? // Might not have been created when deferred rendering
$('<td/>').html( _fnGetCellData( settings, idx, colIdx, 'display' ) )[0] :
data.anCells[ colIdx ];
}
/**
* Get the maximum strlen for each data column
* @param {object} settings dataTables settings object
* @param {int} colIdx column of interest
* @returns {string} max string length for each column
* @memberof DataTable#oApi
*/
function _fnGetMaxLenString( settings, colIdx )
{
var s, max=-1, maxIdx = -1;
for ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
s = _fnGetCellData( settings, i, colIdx, 'display' )+'';
s = s.replace( __re_html_remove, '' );
s = s.replace( / /g, ' ' );
if ( s.length > max ) {
max = s.length;
maxIdx = i;
}
}
return maxIdx;
}
/**
* Append a CSS unit (only if required) to a string
* @param {string} value to css-ify
* @returns {string} value with css unit
* @memberof DataTable#oApi
*/
function _fnStringToCss( s )
{
if ( s === null ) {
return '0px';
}
if ( typeof s == 'number' ) {
return s < 0 ?
'0px' :
s+'px';
}
// Check it has a unit character already
return s.match(/\d$/) ?
s+'px' :
s;
}
|