File: core.sizing.js

package info (click to toggle)
datatables.js 1.11.5%2Bdfsg-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, forky, sid, trixie
  • size: 22,848 kB
  • sloc: javascript: 65,075; xml: 10,712; php: 4,741; sh: 544; makefile: 18
file content (336 lines) | stat: -rw-r--r-- 9,815 bytes parent folder | download | duplicates (6)
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( /&nbsp;/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;
}