0
|
1 /**
|
|
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
|
|
7 */
|
|
8 function _fnConvertToWidth ( sWidth, nParent )
|
|
9 {
|
|
10 if ( !sWidth || sWidth === null || sWidth === '' )
|
|
11 {
|
|
12 return 0;
|
|
13 }
|
|
14
|
|
15 if ( !nParent )
|
|
16 {
|
|
17 nParent = document.body;
|
|
18 }
|
|
19
|
|
20 var iWidth;
|
|
21 var nTmp = document.createElement( "div" );
|
|
22 nTmp.style.width = _fnStringToCss( sWidth );
|
|
23
|
|
24 nParent.appendChild( nTmp );
|
|
25 iWidth = nTmp.offsetWidth;
|
|
26 nParent.removeChild( nTmp );
|
|
27
|
|
28 return ( iWidth );
|
|
29 }
|
|
30
|
|
31
|
|
32 /**
|
|
33 * Calculate the width of columns for the table
|
|
34 * @param {object} oSettings dataTables settings object
|
|
35 * @memberof DataTable#oApi
|
|
36 */
|
|
37 function _fnCalculateColumnWidths ( oSettings )
|
|
38 {
|
|
39 var iTableWidth = oSettings.nTable.offsetWidth;
|
|
40 var iUserInputs = 0;
|
|
41 var iTmpWidth;
|
|
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;
|
|
48
|
|
49 /* Convert any user input sizes into pixel sizes */
|
|
50 for ( i=0 ; i<iColums ; i++ )
|
|
51 {
|
|
52 if ( oSettings.aoColumns[i].bVisible )
|
|
53 {
|
|
54 iVisibleColumns++;
|
|
55
|
|
56 if ( oSettings.aoColumns[i].sWidth !== null )
|
|
57 {
|
|
58 iTmpWidth = _fnConvertToWidth( oSettings.aoColumns[i].sWidthOrig,
|
|
59 nWrapper );
|
|
60 if ( iTmpWidth !== null )
|
|
61 {
|
|
62 oSettings.aoColumns[i].sWidth = _fnStringToCss( iTmpWidth );
|
|
63 }
|
|
64
|
|
65 iUserInputs++;
|
|
66 }
|
|
67 }
|
|
68 }
|
|
69
|
|
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
|
|
73 */
|
|
74 if ( iColums == oHeaders.length && iUserInputs === 0 && iVisibleColumns == iColums &&
|
|
75 oSettings.oScroll.sX === "" && oSettings.oScroll.sY === "" )
|
|
76 {
|
|
77 for ( i=0 ; i<oSettings.aoColumns.length ; i++ )
|
|
78 {
|
|
79 iTmpWidth = $(oHeaders[i]).width();
|
|
80 if ( iTmpWidth !== null )
|
|
81 {
|
|
82 oSettings.aoColumns[i].sWidth = _fnStringToCss( iTmpWidth );
|
|
83 }
|
|
84 }
|
|
85 }
|
|
86 else
|
|
87 {
|
|
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.
|
|
92 */
|
|
93 var
|
|
94 nCalcTmp = oSettings.nTable.cloneNode( false ),
|
|
95 nTheadClone = oSettings.nTHead.cloneNode(true),
|
|
96 nBody = document.createElement( 'tbody' ),
|
|
97 nTr = document.createElement( 'tr' ),
|
|
98 nDivSizing;
|
|
99
|
|
100 nCalcTmp.removeAttribute( "id" );
|
|
101 nCalcTmp.appendChild( nTheadClone );
|
|
102 if ( oSettings.nTFoot !== null )
|
|
103 {
|
|
104 nCalcTmp.appendChild( oSettings.nTFoot.cloneNode(true) );
|
|
105 _fnApplyToChildren( function(n) {
|
|
106 n.style.width = "";
|
|
107 }, nCalcTmp.getElementsByTagName('tr') );
|
|
108 }
|
|
109
|
|
110 nCalcTmp.appendChild( nBody );
|
|
111 nBody.appendChild( nTr );
|
|
112
|
|
113 /* Remove any sizing that was previously applied by the styles */
|
|
114 var jqColSizing = $('thead th', nCalcTmp);
|
|
115 if ( jqColSizing.length === 0 )
|
|
116 {
|
|
117 jqColSizing = $('tbody tr:eq(0)>td', nCalcTmp);
|
|
118 }
|
|
119
|
|
120 /* Apply custom sizing to the cloned header */
|
|
121 var nThs = _fnGetUniqueThs( oSettings, nTheadClone );
|
|
122 iCorrector = 0;
|
|
123 for ( i=0 ; i<iColums ; i++ )
|
|
124 {
|
|
125 var oColumn = oSettings.aoColumns[i];
|
|
126 if ( oColumn.bVisible && oColumn.sWidthOrig !== null && oColumn.sWidthOrig !== "" )
|
|
127 {
|
|
128 nThs[i-iCorrector].style.width = _fnStringToCss( oColumn.sWidthOrig );
|
|
129 }
|
|
130 else if ( oColumn.bVisible )
|
|
131 {
|
|
132 nThs[i-iCorrector].style.width = "";
|
|
133 }
|
|
134 else
|
|
135 {
|
|
136 iCorrector++;
|
|
137 }
|
|
138 }
|
|
139
|
|
140 /* Find the biggest td for each column and put it into the table */
|
|
141 for ( i=0 ; i<iColums ; i++ )
|
|
142 {
|
|
143 if ( oSettings.aoColumns[i].bVisible )
|
|
144 {
|
|
145 var nTd = _fnGetWidestNode( oSettings, i );
|
|
146 if ( nTd !== null )
|
|
147 {
|
|
148 nTd = nTd.cloneNode(true);
|
|
149 if ( oSettings.aoColumns[i].sContentPadding !== "" )
|
|
150 {
|
|
151 nTd.innerHTML += oSettings.aoColumns[i].sContentPadding;
|
|
152 }
|
|
153 nTr.appendChild( nTd );
|
|
154 }
|
|
155 }
|
|
156 }
|
|
157
|
|
158 /* Build the table and 'display' it */
|
|
159 nWrapper.appendChild( nCalcTmp );
|
|
160
|
|
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
|
|
164 */
|
|
165 if ( oSettings.oScroll.sX !== "" && oSettings.oScroll.sXInner !== "" )
|
|
166 {
|
|
167 nCalcTmp.style.width = _fnStringToCss(oSettings.oScroll.sXInner);
|
|
168 }
|
|
169 else if ( oSettings.oScroll.sX !== "" )
|
|
170 {
|
|
171 nCalcTmp.style.width = "";
|
|
172 if ( $(nCalcTmp).width() < nWrapper.offsetWidth )
|
|
173 {
|
|
174 nCalcTmp.style.width = _fnStringToCss( nWrapper.offsetWidth );
|
|
175 }
|
|
176 }
|
|
177 else if ( oSettings.oScroll.sY !== "" )
|
|
178 {
|
|
179 nCalcTmp.style.width = _fnStringToCss( nWrapper.offsetWidth );
|
|
180 }
|
|
181 else if ( widthAttr )
|
|
182 {
|
|
183 nCalcTmp.style.width = _fnStringToCss( widthAttr );
|
|
184 }
|
|
185 nCalcTmp.style.visibility = "hidden";
|
|
186
|
|
187 /* Scrolling considerations */
|
|
188 _fnScrollingWidthAdjust( oSettings, nCalcTmp );
|
|
189
|
|
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
|
|
193 */
|
|
194 var oNodes = $("tbody tr:eq(0)", nCalcTmp).children();
|
|
195 if ( oNodes.length === 0 )
|
|
196 {
|
|
197 oNodes = _fnGetUniqueThs( oSettings, $('thead', nCalcTmp)[0] );
|
|
198 }
|
|
199
|
|
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
|
|
204 */
|
|
205 if ( oSettings.oScroll.sX !== "" )
|
|
206 {
|
|
207 var iTotal = 0;
|
|
208 iCorrector = 0;
|
|
209 for ( i=0 ; i<oSettings.aoColumns.length ; i++ )
|
|
210 {
|
|
211 if ( oSettings.aoColumns[i].bVisible )
|
|
212 {
|
|
213 if ( oSettings.aoColumns[i].sWidthOrig === null )
|
|
214 {
|
|
215 iTotal += $(oNodes[iCorrector]).outerWidth();
|
|
216 }
|
|
217 else
|
|
218 {
|
|
219 iTotal += parseInt(oSettings.aoColumns[i].sWidth.replace('px',''), 10) +
|
|
220 ($(oNodes[iCorrector]).outerWidth() - $(oNodes[iCorrector]).width());
|
|
221 }
|
|
222 iCorrector++;
|
|
223 }
|
|
224 }
|
|
225
|
|
226 nCalcTmp.style.width = _fnStringToCss( iTotal );
|
|
227 oSettings.nTable.style.width = _fnStringToCss( iTotal );
|
|
228 }
|
|
229
|
|
230 iCorrector = 0;
|
|
231 for ( i=0 ; i<oSettings.aoColumns.length ; i++ )
|
|
232 {
|
|
233 if ( oSettings.aoColumns[i].bVisible )
|
|
234 {
|
|
235 iWidth = $(oNodes[iCorrector]).width();
|
|
236 if ( iWidth !== null && iWidth > 0 )
|
|
237 {
|
|
238 oSettings.aoColumns[i].sWidth = _fnStringToCss( iWidth );
|
|
239 }
|
|
240 iCorrector++;
|
|
241 }
|
|
242 }
|
|
243
|
|
244 var cssWidth = $(nCalcTmp).css('width');
|
|
245 oSettings.nTable.style.width = (cssWidth.indexOf('%') !== -1) ?
|
|
246 cssWidth : _fnStringToCss( $(nCalcTmp).outerWidth() );
|
|
247 nCalcTmp.parentNode.removeChild( nCalcTmp );
|
|
248 }
|
|
249
|
|
250 if ( widthAttr )
|
|
251 {
|
|
252 oSettings.nTable.style.width = _fnStringToCss( widthAttr );
|
|
253 }
|
|
254 }
|
|
255
|
|
256
|
|
257 /**
|
|
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
|
|
262 */
|
|
263 function _fnScrollingWidthAdjust ( oSettings, n )
|
|
264 {
|
|
265 if ( oSettings.oScroll.sX === "" && oSettings.oScroll.sY !== "" )
|
|
266 {
|
|
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.
|
|
269 */
|
|
270 var iOrigWidth = $(n).width();
|
|
271 n.style.width = _fnStringToCss( $(n).outerWidth()-oSettings.oScroll.iBarWidth );
|
|
272 }
|
|
273 else if ( oSettings.oScroll.sX !== "" )
|
|
274 {
|
|
275 /* When x-scrolling both ways, fix the table at it's current size, without adjusting */
|
|
276 n.style.width = _fnStringToCss( $(n).outerWidth() );
|
|
277 }
|
|
278 }
|
|
279
|
|
280
|
|
281 /**
|
|
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
|
|
287 */
|
|
288 function _fnGetWidestNode( oSettings, iCol )
|
|
289 {
|
|
290 var iMaxIndex = _fnGetMaxLenString( oSettings, iCol );
|
|
291 if ( iMaxIndex < 0 )
|
|
292 {
|
|
293 return null;
|
|
294 }
|
|
295
|
|
296 if ( oSettings.aoData[iMaxIndex].nTr === null )
|
|
297 {
|
|
298 var n = document.createElement('td');
|
|
299 n.innerHTML = _fnGetCellData( oSettings, iMaxIndex, iCol, '' );
|
|
300 return n;
|
|
301 }
|
|
302 return _fnGetTdNodes(oSettings, iMaxIndex)[iCol];
|
|
303 }
|
|
304
|
|
305
|
|
306 /**
|
|
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
|
|
312 */
|
|
313 function _fnGetMaxLenString( oSettings, iCol )
|
|
314 {
|
|
315 var iMax = -1;
|
|
316 var iMaxIndex = -1;
|
|
317
|
|
318 for ( var i=0 ; i<oSettings.aoData.length ; i++ )
|
|
319 {
|
|
320 var s = _fnGetCellData( oSettings, i, iCol, 'display' )+"";
|
|
321 s = s.replace( /<.*?>/g, "" );
|
|
322 if ( s.length > iMax )
|
|
323 {
|
|
324 iMax = s.length;
|
|
325 iMaxIndex = i;
|
|
326 }
|
|
327 }
|
|
328
|
|
329 return iMaxIndex;
|
|
330 }
|
|
331
|
|
332
|
|
333 /**
|
|
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
|
|
339 */
|
|
340 function _fnStringToCss( s )
|
|
341 {
|
|
342 if ( s === null )
|
|
343 {
|
|
344 return "0px";
|
|
345 }
|
|
346
|
|
347 if ( typeof s == 'number' )
|
|
348 {
|
|
349 if ( s < 0 )
|
|
350 {
|
|
351 return "0px";
|
|
352 }
|
|
353 return s+"px";
|
|
354 }
|
|
355
|
|
356 /* Check if the last character is not 0-9 */
|
|
357 var c = s.charCodeAt( s.length-1 );
|
|
358 if (c < 0x30 || c > 0x39)
|
|
359 {
|
|
360 return s;
|
|
361 }
|
|
362 return s+"px";
|
|
363 }
|
|
364
|
|
365
|
|
366 /**
|
|
367 * Get the width of a scroll bar in this browser being used
|
|
368 * @returns {int} width in pixels
|
|
369 * @memberof DataTable#oApi
|
|
370 */
|
|
371 function _fnScrollBarWidth ()
|
|
372 {
|
|
373 var inner = document.createElement('p');
|
|
374 var style = inner.style;
|
|
375 style.width = "100%";
|
|
376 style.height = "200px";
|
|
377 style.padding = "0px";
|
|
378
|
|
379 var outer = document.createElement('div');
|
|
380 style = outer.style;
|
|
381 style.position = "absolute";
|
|
382 style.top = "0px";
|
|
383 style.left = "0px";
|
|
384 style.visibility = "hidden";
|
|
385 style.width = "200px";
|
|
386 style.height = "150px";
|
|
387 style.padding = "0px";
|
|
388 style.overflow = "hidden";
|
|
389 outer.appendChild(inner);
|
|
390
|
|
391 document.body.appendChild(outer);
|
|
392 var w1 = inner.offsetWidth;
|
|
393 outer.style.overflow = 'scroll';
|
|
394 var w2 = inner.offsetWidth;
|
|
395 if ( w1 == w2 )
|
|
396 {
|
|
397 w2 = outer.clientWidth;
|
|
398 }
|
|
399
|
|
400 document.body.removeChild(outer);
|
|
401 return (w1 - w2);
|
|
402 }
|
|
403
|