1 |
/** |
2 |
* Create a new TR element (and it's TD children) for a row |
3 |
* @param {object} oSettings dataTables settings object |
4 |
* @param {int} iRow Row to consider |
5 |
* @memberof DataTable#oApi |
6 |
*/ |
7 |
function _fnCreateTr ( oSettings, iRow ) |
8 |
{ |
9 |
var oData = oSettings.aoData[iRow]; |
10 |
var nTd; |
11 |
|
12 |
if ( oData.nTr === null ) |
13 |
{ |
14 |
oData.nTr = document.createElement('tr'); |
15 |
|
16 |
/* Use a private property on the node to allow reserve mapping from the node |
17 |
* to the aoData array for fast look up |
18 |
*/ |
19 |
oData.nTr._DT_RowIndex = iRow; |
20 |
|
21 |
/* Special parameters can be given by the data source to be used on the row */ |
22 |
if ( oData._aData.DT_RowId ) |
23 |
{ |
24 |
oData.nTr.id = oData._aData.DT_RowId; |
25 |
} |
26 |
|
27 |
if ( oData._aData.DT_RowClass ) |
28 |
{ |
29 |
oData.nTr.className = oData._aData.DT_RowClass; |
30 |
} |
31 |
|
32 |
/* Process each column */ |
33 |
for ( var i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ ) |
34 |
{ |
35 |
var oCol = oSettings.aoColumns[i]; |
36 |
nTd = document.createElement( oCol.sCellType ); |
37 |
|
38 |
/* Render if needed - if bUseRendered is true then we already have the rendered |
39 |
* value in the data source - so can just use that |
40 |
*/ |
41 |
nTd.innerHTML = (typeof oCol.fnRender === 'function' && (!oCol.bUseRendered || oCol.mData === null)) ? |
42 |
_fnRender( oSettings, iRow, i ) : |
43 |
_fnGetCellData( oSettings, iRow, i, 'display' ); |
44 |
|
45 |
/* Add user defined class */ |
46 |
if ( oCol.sClass !== null ) |
47 |
{ |
48 |
nTd.className = oCol.sClass; |
49 |
} |
50 |
|
51 |
if ( oCol.bVisible ) |
52 |
{ |
53 |
oData.nTr.appendChild( nTd ); |
54 |
oData._anHidden[i] = null; |
55 |
} |
56 |
else |
57 |
{ |
58 |
oData._anHidden[i] = nTd; |
59 |
} |
60 |
|
61 |
if ( oCol.fnCreatedCell ) |
62 |
{ |
63 |
oCol.fnCreatedCell.call( oSettings.oInstance, |
64 |
nTd, _fnGetCellData( oSettings, iRow, i, 'display' ), oData._aData, iRow, i |
65 |
); |
66 |
} |
67 |
} |
68 |
|
69 |
_fnCallbackFire( oSettings, 'aoRowCreatedCallback', null, [oData.nTr, oData._aData, iRow] ); |
70 |
} |
71 |
} |
72 |
|
73 |
|
74 |
/** |
75 |
* Create the HTML header for the table |
76 |
* @param {object} oSettings dataTables settings object |
77 |
* @memberof DataTable#oApi |
78 |
*/ |
79 |
function _fnBuildHead( oSettings ) |
80 |
{ |
81 |
var i, nTh, iLen, j, jLen; |
82 |
var iThs = $('th, td', oSettings.nTHead).length; |
83 |
var iCorrector = 0; |
84 |
var jqChildren; |
85 |
|
86 |
/* If there is a header in place - then use it - otherwise it's going to get nuked... */ |
87 |
if ( iThs !== 0 ) |
88 |
{ |
89 |
/* We've got a thead from the DOM, so remove hidden columns and apply width to vis cols */ |
90 |
for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ ) |
91 |
{ |
92 |
nTh = oSettings.aoColumns[i].nTh; |
93 |
nTh.setAttribute('role', 'columnheader'); |
94 |
if ( oSettings.aoColumns[i].bSortable ) |
95 |
{ |
96 |
nTh.setAttribute('tabindex', oSettings.iTabIndex); |
97 |
nTh.setAttribute('aria-controls', oSettings.sTableId); |
98 |
} |
99 |
|
100 |
if ( oSettings.aoColumns[i].sClass !== null ) |
101 |
{ |
102 |
$(nTh).addClass( oSettings.aoColumns[i].sClass ); |
103 |
} |
104 |
|
105 |
/* Set the title of the column if it is user defined (not what was auto detected) */ |
106 |
if ( oSettings.aoColumns[i].sTitle != nTh.innerHTML ) |
107 |
{ |
108 |
nTh.innerHTML = oSettings.aoColumns[i].sTitle; |
109 |
} |
110 |
} |
111 |
} |
112 |
else |
113 |
{ |
114 |
/* We don't have a header in the DOM - so we are going to have to create one */ |
115 |
var nTr = document.createElement( "tr" ); |
116 |
|
117 |
for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ ) |
118 |
{ |
119 |
nTh = oSettings.aoColumns[i].nTh; |
120 |
nTh.innerHTML = oSettings.aoColumns[i].sTitle; |
121 |
nTh.setAttribute('tabindex', '0'); |
122 |
|
123 |
if ( oSettings.aoColumns[i].sClass !== null ) |
124 |
{ |
125 |
$(nTh).addClass( oSettings.aoColumns[i].sClass ); |
126 |
} |
127 |
|
128 |
nTr.appendChild( nTh ); |
129 |
} |
130 |
$(oSettings.nTHead).html( '' )[0].appendChild( nTr ); |
131 |
_fnDetectHeader( oSettings.aoHeader, oSettings.nTHead ); |
132 |
} |
133 |
|
134 |
/* ARIA role for the rows */ |
135 |
$(oSettings.nTHead).children('tr').attr('role', 'row'); |
136 |
|
137 |
/* Add the extra markup needed by jQuery UI's themes */ |
138 |
if ( oSettings.bJUI ) |
139 |
{ |
140 |
for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ ) |
141 |
{ |
142 |
nTh = oSettings.aoColumns[i].nTh; |
143 |
|
144 |
var nDiv = document.createElement('div'); |
145 |
nDiv.className = oSettings.oClasses.sSortJUIWrapper; |
146 |
$(nTh).contents().appendTo(nDiv); |
147 |
|
148 |
var nSpan = document.createElement('span'); |
149 |
nSpan.className = oSettings.oClasses.sSortIcon; |
150 |
nDiv.appendChild( nSpan ); |
151 |
nTh.appendChild( nDiv ); |
152 |
} |
153 |
} |
154 |
|
155 |
if ( oSettings.oFeatures.bSort ) |
156 |
{ |
157 |
for ( i=0 ; i<oSettings.aoColumns.length ; i++ ) |
158 |
{ |
159 |
if ( oSettings.aoColumns[i].bSortable !== false ) |
160 |
{ |
161 |
_fnSortAttachListener( oSettings, oSettings.aoColumns[i].nTh, i ); |
162 |
} |
163 |
else |
164 |
{ |
165 |
$(oSettings.aoColumns[i].nTh).addClass( oSettings.oClasses.sSortableNone ); |
166 |
} |
167 |
} |
168 |
} |
169 |
|
170 |
/* Deal with the footer - add classes if required */ |
171 |
if ( oSettings.oClasses.sFooterTH !== "" ) |
172 |
{ |
173 |
$(oSettings.nTFoot).children('tr').children('th').addClass( oSettings.oClasses.sFooterTH ); |
174 |
} |
175 |
|
176 |
/* Cache the footer elements */ |
177 |
if ( oSettings.nTFoot !== null ) |
178 |
{ |
179 |
var anCells = _fnGetUniqueThs( oSettings, null, oSettings.aoFooter ); |
180 |
for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ ) |
181 |
{ |
182 |
if ( anCells[i] ) |
183 |
{ |
184 |
oSettings.aoColumns[i].nTf = anCells[i]; |
185 |
if ( oSettings.aoColumns[i].sClass ) |
186 |
{ |
187 |
$(anCells[i]).addClass( oSettings.aoColumns[i].sClass ); |
188 |
} |
189 |
} |
190 |
} |
191 |
} |
192 |
} |
193 |
|
194 |
|
195 |
/** |
196 |
* Draw the header (or footer) element based on the column visibility states. The |
197 |
* methodology here is to use the layout array from _fnDetectHeader, modified for |
198 |
* the instantaneous column visibility, to construct the new layout. The grid is |
199 |
* traversed over cell at a time in a rows x columns grid fashion, although each |
200 |
* cell insert can cover multiple elements in the grid - which is tracks using the |
201 |
* aApplied array. Cell inserts in the grid will only occur where there isn't |
202 |
* already a cell in that position. |
203 |
* @param {object} oSettings dataTables settings object |
204 |
* @param array {objects} aoSource Layout array from _fnDetectHeader |
205 |
* @param {boolean} [bIncludeHidden=false] If true then include the hidden columns in the calc, |
206 |
* @memberof DataTable#oApi |
207 |
*/ |
208 |
function _fnDrawHead( oSettings, aoSource, bIncludeHidden ) |
209 |
{ |
210 |
var i, iLen, j, jLen, k, kLen, n, nLocalTr; |
211 |
var aoLocal = []; |
212 |
var aApplied = []; |
213 |
var iColumns = oSettings.aoColumns.length; |
214 |
var iRowspan, iColspan; |
215 |
|
216 |
if ( bIncludeHidden === undefined ) |
217 |
{ |
218 |
bIncludeHidden = false; |
219 |
} |
220 |
|
221 |
/* Make a copy of the master layout array, but without the visible columns in it */ |
222 |
for ( i=0, iLen=aoSource.length ; i<iLen ; i++ ) |
223 |
{ |
224 |
aoLocal[i] = aoSource[i].slice(); |
225 |
aoLocal[i].nTr = aoSource[i].nTr; |
226 |
|
227 |
/* Remove any columns which are currently hidden */ |
228 |
for ( j=iColumns-1 ; j>=0 ; j-- ) |
229 |
{ |
230 |
if ( !oSettings.aoColumns[j].bVisible && !bIncludeHidden ) |
231 |
{ |
232 |
aoLocal[i].splice( j, 1 ); |
233 |
} |
234 |
} |
235 |
|
236 |
/* Prep the applied array - it needs an element for each row */ |
237 |
aApplied.push( [] ); |
238 |
} |
239 |
|
240 |
for ( i=0, iLen=aoLocal.length ; i<iLen ; i++ ) |
241 |
{ |
242 |
nLocalTr = aoLocal[i].nTr; |
243 |
|
244 |
/* All cells are going to be replaced, so empty out the row */ |
245 |
if ( nLocalTr ) |
246 |
{ |
247 |
while( (n = nLocalTr.firstChild) ) |
248 |
{ |
249 |
nLocalTr.removeChild( n ); |
250 |
} |
251 |
} |
252 |
|
253 |
for ( j=0, jLen=aoLocal[i].length ; j<jLen ; j++ ) |
254 |
{ |
255 |
iRowspan = 1; |
256 |
iColspan = 1; |
257 |
|
258 |
/* Check to see if there is already a cell (row/colspan) covering our target |
259 |
* insert point. If there is, then there is nothing to do. |
260 |
*/ |
261 |
if ( aApplied[i][j] === undefined ) |
262 |
{ |
263 |
nLocalTr.appendChild( aoLocal[i][j].cell ); |
264 |
aApplied[i][j] = 1; |
265 |
|
266 |
/* Expand the cell to cover as many rows as needed */ |
267 |
while ( aoLocal[i+iRowspan] !== undefined && |
268 |
aoLocal[i][j].cell == aoLocal[i+iRowspan][j].cell ) |
269 |
{ |
270 |
aApplied[i+iRowspan][j] = 1; |
271 |
iRowspan++; |
272 |
} |
273 |
|
274 |
/* Expand the cell to cover as many columns as needed */ |
275 |
while ( aoLocal[i][j+iColspan] !== undefined && |
276 |
aoLocal[i][j].cell == aoLocal[i][j+iColspan].cell ) |
277 |
{ |
278 |
/* Must update the applied array over the rows for the columns */ |
279 |
for ( k=0 ; k<iRowspan ; k++ ) |
280 |
{ |
281 |
aApplied[i+k][j+iColspan] = 1; |
282 |
} |
283 |
iColspan++; |
284 |
} |
285 |
|
286 |
/* Do the actual expansion in the DOM */ |
287 |
aoLocal[i][j].cell.rowSpan = iRowspan; |
288 |
aoLocal[i][j].cell.colSpan = iColspan; |
289 |
} |
290 |
} |
291 |
} |
292 |
} |
293 |
|
294 |
|
295 |
/** |
296 |
* Insert the required TR nodes into the table for display |
297 |
* @param {object} oSettings dataTables settings object |
298 |
* @memberof DataTable#oApi |
299 |
*/ |
300 |
function _fnDraw( oSettings ) |
301 |
{ |
302 |
/* Provide a pre-callback function which can be used to cancel the draw is false is returned */ |
303 |
var aPreDraw = _fnCallbackFire( oSettings, 'aoPreDrawCallback', 'preDraw', [oSettings] ); |
304 |
if ( $.inArray( false, aPreDraw ) !== -1 ) |
305 |
{ |
306 |
_fnProcessingDisplay( oSettings, false ); |
307 |
return; |
308 |
} |
309 |
|
310 |
var i, iLen, n; |
311 |
var anRows = []; |
312 |
var iRowCount = 0; |
313 |
var iStripes = oSettings.asStripeClasses.length; |
314 |
var iOpenRows = oSettings.aoOpenRows.length; |
315 |
|
316 |
oSettings.bDrawing = true; |
317 |
|
318 |
/* Check and see if we have an initial draw position from state saving */ |
319 |
if ( oSettings.iInitDisplayStart !== undefined && oSettings.iInitDisplayStart != -1 ) |
320 |
{ |
321 |
if ( oSettings.oFeatures.bServerSide ) |
322 |
{ |
323 |
oSettings._iDisplayStart = oSettings.iInitDisplayStart; |
324 |
} |
325 |
else |
326 |
{ |
327 |
oSettings._iDisplayStart = (oSettings.iInitDisplayStart >= oSettings.fnRecordsDisplay()) ? |
328 |
0 : oSettings.iInitDisplayStart; |
329 |
} |
330 |
oSettings.iInitDisplayStart = -1; |
331 |
_fnCalculateEnd( oSettings ); |
332 |
} |
333 |
|
334 |
/* Server-side processing draw intercept */ |
335 |
if ( oSettings.bDeferLoading ) |
336 |
{ |
337 |
oSettings.bDeferLoading = false; |
338 |
oSettings.iDraw++; |
339 |
} |
340 |
else if ( !oSettings.oFeatures.bServerSide ) |
341 |
{ |
342 |
oSettings.iDraw++; |
343 |
} |
344 |
else if ( !oSettings.bDestroying && !_fnAjaxUpdate( oSettings ) ) |
345 |
{ |
346 |
return; |
347 |
} |
348 |
|
349 |
if ( oSettings.aiDisplay.length !== 0 ) |
350 |
{ |
351 |
var iStart = oSettings._iDisplayStart; |
352 |
var iEnd = oSettings._iDisplayEnd; |
353 |
|
354 |
if ( oSettings.oFeatures.bServerSide ) |
355 |
{ |
356 |
iStart = 0; |
357 |
iEnd = oSettings.aoData.length; |
358 |
} |
359 |
|
360 |
for ( var j=iStart ; j<iEnd ; j++ ) |
361 |
{ |
362 |
var aoData = oSettings.aoData[ oSettings.aiDisplay[j] ]; |
363 |
if ( aoData.nTr === null ) |
364 |
{ |
365 |
_fnCreateTr( oSettings, oSettings.aiDisplay[j] ); |
366 |
} |
367 |
|
368 |
var nRow = aoData.nTr; |
369 |
|
370 |
/* Remove the old striping classes and then add the new one */ |
371 |
if ( iStripes !== 0 ) |
372 |
{ |
373 |
var sStripe = oSettings.asStripeClasses[ iRowCount % iStripes ]; |
374 |
if ( aoData._sRowStripe != sStripe ) |
375 |
{ |
376 |
$(nRow).removeClass( aoData._sRowStripe ).addClass( sStripe ); |
377 |
aoData._sRowStripe = sStripe; |
378 |
} |
379 |
} |
380 |
|
381 |
/* Row callback functions - might want to manipulate the row */ |
382 |
_fnCallbackFire( oSettings, 'aoRowCallback', null, |
383 |
[nRow, oSettings.aoData[ oSettings.aiDisplay[j] ]._aData, iRowCount, j] ); |
384 |
|
385 |
anRows.push( nRow ); |
386 |
iRowCount++; |
387 |
|
388 |
/* If there is an open row - and it is attached to this parent - attach it on redraw */ |
389 |
if ( iOpenRows !== 0 ) |
390 |
{ |
391 |
for ( var k=0 ; k<iOpenRows ; k++ ) |
392 |
{ |
393 |
if ( nRow == oSettings.aoOpenRows[k].nParent ) |
394 |
{ |
395 |
anRows.push( oSettings.aoOpenRows[k].nTr ); |
396 |
break; |
397 |
} |
398 |
} |
399 |
} |
400 |
} |
401 |
} |
402 |
else |
403 |
{ |
404 |
/* Table is empty - create a row with an empty message in it */ |
405 |
anRows[ 0 ] = document.createElement( 'tr' ); |
406 |
|
407 |
if ( oSettings.asStripeClasses[0] ) |
408 |
{ |
409 |
anRows[ 0 ].className = oSettings.asStripeClasses[0]; |
410 |
} |
411 |
|
412 |
var oLang = oSettings.oLanguage; |
413 |
var sZero = oLang.sZeroRecords; |
414 |
if ( oSettings.iDraw == 1 && oSettings.sAjaxSource !== null && !oSettings.oFeatures.bServerSide ) |
415 |
{ |
416 |
sZero = oLang.sLoadingRecords; |
417 |
} |
418 |
else if ( oLang.sEmptyTable && oSettings.fnRecordsTotal() === 0 ) |
419 |
{ |
420 |
sZero = oLang.sEmptyTable; |
421 |
} |
422 |
|
423 |
var nTd = document.createElement( 'td' ); |
424 |
nTd.setAttribute( 'valign', "top" ); |
425 |
nTd.colSpan = _fnVisbleColumns( oSettings ); |
426 |
nTd.className = oSettings.oClasses.sRowEmpty; |
427 |
nTd.innerHTML = _fnInfoMacros( oSettings, sZero ); |
428 |
|
429 |
anRows[ iRowCount ].appendChild( nTd ); |
430 |
} |
431 |
|
432 |
/* Header and footer callbacks */ |
433 |
_fnCallbackFire( oSettings, 'aoHeaderCallback', 'header', [ $(oSettings.nTHead).children('tr')[0], |
434 |
_fnGetDataMaster( oSettings ), oSettings._iDisplayStart, oSettings.fnDisplayEnd(), oSettings.aiDisplay ] ); |
435 |
|
436 |
_fnCallbackFire( oSettings, 'aoFooterCallback', 'footer', [ $(oSettings.nTFoot).children('tr')[0], |
437 |
_fnGetDataMaster( oSettings ), oSettings._iDisplayStart, oSettings.fnDisplayEnd(), oSettings.aiDisplay ] ); |
438 |
|
439 |
/* |
440 |
* Need to remove any old row from the display - note we can't just empty the tbody using |
441 |
* $().html('') since this will unbind the jQuery event handlers (even although the node |
442 |
* still exists!) - equally we can't use innerHTML, since IE throws an exception. |
443 |
*/ |
444 |
var |
445 |
nAddFrag = document.createDocumentFragment(), |
446 |
nRemoveFrag = document.createDocumentFragment(), |
447 |
nBodyPar, nTrs; |
448 |
|
449 |
if ( oSettings.nTBody ) |
450 |
{ |
451 |
nBodyPar = oSettings.nTBody.parentNode; |
452 |
nRemoveFrag.appendChild( oSettings.nTBody ); |
453 |
|
454 |
/* When doing infinite scrolling, only remove child rows when sorting, filtering or start |
455 |
* up. When not infinite scroll, always do it. |
456 |
*/ |
457 |
if ( !oSettings.oScroll.bInfinite || !oSettings._bInitComplete || |
458 |
oSettings.bSorted || oSettings.bFiltered ) |
459 |
{ |
460 |
while( (n = oSettings.nTBody.firstChild) ) |
461 |
{ |
462 |
oSettings.nTBody.removeChild( n ); |
463 |
} |
464 |
} |
465 |
|
466 |
/* Put the draw table into the dom */ |
467 |
for ( i=0, iLen=anRows.length ; i<iLen ; i++ ) |
468 |
{ |
469 |
nAddFrag.appendChild( anRows[i] ); |
470 |
} |
471 |
|
472 |
oSettings.nTBody.appendChild( nAddFrag ); |
473 |
if ( nBodyPar !== null ) |
474 |
{ |
475 |
nBodyPar.appendChild( oSettings.nTBody ); |
476 |
} |
477 |
} |
478 |
|
479 |
/* Call all required callback functions for the end of a draw */ |
480 |
_fnCallbackFire( oSettings, 'aoDrawCallback', 'draw', [oSettings] ); |
481 |
|
482 |
/* Draw is complete, sorting and filtering must be as well */ |
483 |
oSettings.bSorted = false; |
484 |
oSettings.bFiltered = false; |
485 |
oSettings.bDrawing = false; |
486 |
|
487 |
if ( oSettings.oFeatures.bServerSide ) |
488 |
{ |
489 |
_fnProcessingDisplay( oSettings, false ); |
490 |
if ( !oSettings._bInitComplete ) |
491 |
{ |
492 |
_fnInitComplete( oSettings ); |
493 |
} |
494 |
} |
495 |
} |
496 |
|
497 |
|
498 |
/** |
499 |
* Redraw the table - taking account of the various features which are enabled |
500 |
* @param {object} oSettings dataTables settings object |
501 |
* @memberof DataTable#oApi |
502 |
*/ |
503 |
function _fnReDraw( oSettings ) |
504 |
{ |
505 |
if ( oSettings.oFeatures.bSort ) |
506 |
{ |
507 |
/* Sorting will refilter and draw for us */ |
508 |
_fnSort( oSettings, oSettings.oPreviousSearch ); |
509 |
} |
510 |
else if ( oSettings.oFeatures.bFilter ) |
511 |
{ |
512 |
/* Filtering will redraw for us */ |
513 |
_fnFilterComplete( oSettings, oSettings.oPreviousSearch ); |
514 |
} |
515 |
else |
516 |
{ |
517 |
_fnCalculateEnd( oSettings ); |
518 |
_fnDraw( oSettings ); |
519 |
} |
520 |
} |
521 |
|
522 |
|
523 |
/** |
524 |
* Add the options to the page HTML for the table |
525 |
* @param {object} oSettings dataTables settings object |
526 |
* @memberof DataTable#oApi |
527 |
*/ |
528 |
function _fnAddOptionsHtml ( oSettings ) |
529 |
{ |
530 |
/* |
531 |
* Create a temporary, empty, div which we can later on replace with what we have generated |
532 |
* we do it this way to rendering the 'options' html offline - speed :-) |
533 |
*/ |
534 |
var nHolding = $('<div></div>')[0]; |
535 |
oSettings.nTable.parentNode.insertBefore( nHolding, oSettings.nTable ); |
536 |
|
537 |
/* |
538 |
* All DataTables are wrapped in a div |
539 |
*/ |
540 |
oSettings.nTableWrapper = $('<div id="'+oSettings.sTableId+'_wrapper" class="'+oSettings.oClasses.sWrapper+'" role="grid"></div>')[0]; |
541 |
oSettings.nTableReinsertBefore = oSettings.nTable.nextSibling; |
542 |
|
543 |
/* Track where we want to insert the option */ |
544 |
var nInsertNode = oSettings.nTableWrapper; |
545 |
|
546 |
/* Loop over the user set positioning and place the elements as needed */ |
547 |
var aDom = oSettings.sDom.split(''); |
548 |
var nTmp, iPushFeature, cOption, nNewNode, cNext, sAttr, j; |
549 |
for ( var i=0 ; i<aDom.length ; i++ ) |
550 |
{ |
551 |
iPushFeature = 0; |
552 |
cOption = aDom[i]; |
553 |
|
554 |
if ( cOption == '<' ) |
555 |
{ |
556 |
/* New container div */ |
557 |
nNewNode = $('<div></div>')[0]; |
558 |
|
559 |
/* Check to see if we should append an id and/or a class name to the container */ |
560 |
cNext = aDom[i+1]; |
561 |
if ( cNext == "'" || cNext == '"' ) |
562 |
{ |
563 |
sAttr = ""; |
564 |
j = 2; |
565 |
while ( aDom[i+j] != cNext ) |
566 |
{ |
567 |
sAttr += aDom[i+j]; |
568 |
j++; |
569 |
} |
570 |
|
571 |
/* Replace jQuery UI constants */ |
572 |
if ( sAttr == "H" ) |
573 |
{ |
574 |
sAttr = oSettings.oClasses.sJUIHeader; |
575 |
} |
576 |
else if ( sAttr == "F" ) |
577 |
{ |
578 |
sAttr = oSettings.oClasses.sJUIFooter; |
579 |
} |
580 |
|
581 |
/* The attribute can be in the format of "#id.class", "#id" or "class" This logic |
582 |
* breaks the string into parts and applies them as needed |
583 |
*/ |
584 |
if ( sAttr.indexOf('.') != -1 ) |
585 |
{ |
586 |
var aSplit = sAttr.split('.'); |
587 |
nNewNode.id = aSplit[0].substr(1, aSplit[0].length-1); |
588 |
nNewNode.className = aSplit[1]; |
589 |
} |
590 |
else if ( sAttr.charAt(0) == "#" ) |
591 |
{ |
592 |
nNewNode.id = sAttr.substr(1, sAttr.length-1); |
593 |
} |
594 |
else |
595 |
{ |
596 |
nNewNode.className = sAttr; |
597 |
} |
598 |
|
599 |
i += j; /* Move along the position array */ |
600 |
} |
601 |
|
602 |
nInsertNode.appendChild( nNewNode ); |
603 |
nInsertNode = nNewNode; |
604 |
} |
605 |
else if ( cOption == '>' ) |
606 |
{ |
607 |
/* End container div */ |
608 |
nInsertNode = nInsertNode.parentNode; |
609 |
} |
610 |
else if ( cOption == 'l' && oSettings.oFeatures.bPaginate && oSettings.oFeatures.bLengthChange ) |
611 |
{ |
612 |
/* Length */ |
613 |
nTmp = _fnFeatureHtmlLength( oSettings ); |
614 |
iPushFeature = 1; |
615 |
} |
616 |
else if ( cOption == 'f' && oSettings.oFeatures.bFilter ) |
617 |
{ |
618 |
/* Filter */ |
619 |
nTmp = _fnFeatureHtmlFilter( oSettings ); |
620 |
iPushFeature = 1; |
621 |
} |
622 |
else if ( cOption == 'r' && oSettings.oFeatures.bProcessing ) |
623 |
{ |
624 |
/* pRocessing */ |
625 |
nTmp = _fnFeatureHtmlProcessing( oSettings ); |
626 |
iPushFeature = 1; |
627 |
} |
628 |
else if ( cOption == 't' ) |
629 |
{ |
630 |
/* Table */ |
631 |
nTmp = _fnFeatureHtmlTable( oSettings ); |
632 |
iPushFeature = 1; |
633 |
} |
634 |
else if ( cOption == 'i' && oSettings.oFeatures.bInfo ) |
635 |
{ |
636 |
/* Info */ |
637 |
nTmp = _fnFeatureHtmlInfo( oSettings ); |
638 |
iPushFeature = 1; |
639 |
} |
640 |
else if ( cOption == 'p' && oSettings.oFeatures.bPaginate ) |
641 |
{ |
642 |
/* Pagination */ |
643 |
nTmp = _fnFeatureHtmlPaginate( oSettings ); |
644 |
iPushFeature = 1; |
645 |
} |
646 |
else if ( DataTable.ext.aoFeatures.length !== 0 ) |
647 |
{ |
648 |
/* Plug-in features */ |
649 |
var aoFeatures = DataTable.ext.aoFeatures; |
650 |
for ( var k=0, kLen=aoFeatures.length ; k<kLen ; k++ ) |
651 |
{ |
652 |
if ( cOption == aoFeatures[k].cFeature ) |
653 |
{ |
654 |
nTmp = aoFeatures[k].fnInit( oSettings ); |
655 |
if ( nTmp ) |
656 |
{ |
657 |
iPushFeature = 1; |
658 |
} |
659 |
break; |
660 |
} |
661 |
} |
662 |
} |
663 |
|
664 |
/* Add to the 2D features array */ |
665 |
if ( iPushFeature == 1 && nTmp !== null ) |
666 |
{ |
667 |
if ( typeof oSettings.aanFeatures[cOption] !== 'object' ) |
668 |
{ |
669 |
oSettings.aanFeatures[cOption] = []; |
670 |
} |
671 |
oSettings.aanFeatures[cOption].push( nTmp ); |
672 |
nInsertNode.appendChild( nTmp ); |
673 |
} |
674 |
} |
675 |
|
676 |
/* Built our DOM structure - replace the holding div with what we want */ |
677 |
nHolding.parentNode.replaceChild( oSettings.nTableWrapper, nHolding ); |
678 |
} |
679 |
|
680 |
|
681 |
/** |
682 |
* Use the DOM source to create up an array of header cells. The idea here is to |
683 |
* create a layout grid (array) of rows x columns, which contains a reference |
684 |
* to the cell that that point in the grid (regardless of col/rowspan), such that |
685 |
* any column / row could be removed and the new grid constructed |
686 |
* @param array {object} aLayout Array to store the calculated layout in |
687 |
* @param {node} nThead The header/footer element for the table |
688 |
* @memberof DataTable#oApi |
689 |
*/ |
690 |
function _fnDetectHeader ( aLayout, nThead ) |
691 |
{ |
692 |
var nTrs = $(nThead).children('tr'); |
693 |
var nTr, nCell; |
694 |
var i, k, l, iLen, jLen, iColShifted, iColumn, iColspan, iRowspan; |
695 |
var bUnique; |
696 |
var fnShiftCol = function ( a, i, j ) { |
697 |
var k = a[i]; |
698 |
while ( k[j] ) { |
699 |
j++; |
700 |
} |
701 |
return j; |
702 |
}; |
703 |
|
704 |
aLayout.splice( 0, aLayout.length ); |
705 |
|
706 |
/* We know how many rows there are in the layout - so prep it */ |
707 |
for ( i=0, iLen=nTrs.length ; i<iLen ; i++ ) |
708 |
{ |
709 |
aLayout.push( [] ); |
710 |
} |
711 |
|
712 |
/* Calculate a layout array */ |
713 |
for ( i=0, iLen=nTrs.length ; i<iLen ; i++ ) |
714 |
{ |
715 |
nTr = nTrs[i]; |
716 |
iColumn = 0; |
717 |
|
718 |
/* For every cell in the row... */ |
719 |
nCell = nTr.firstChild; |
720 |
while ( nCell ) { |
721 |
if ( nCell.nodeName.toUpperCase() == "TD" || |
722 |
nCell.nodeName.toUpperCase() == "TH" ) |
723 |
{ |
724 |
/* Get the col and rowspan attributes from the DOM and sanitise them */ |
725 |
iColspan = nCell.getAttribute('colspan') * 1; |
726 |
iRowspan = nCell.getAttribute('rowspan') * 1; |
727 |
iColspan = (!iColspan || iColspan===0 || iColspan===1) ? 1 : iColspan; |
728 |
iRowspan = (!iRowspan || iRowspan===0 || iRowspan===1) ? 1 : iRowspan; |
729 |
|
730 |
/* There might be colspan cells already in this row, so shift our target |
731 |
* accordingly |
732 |
*/ |
733 |
iColShifted = fnShiftCol( aLayout, i, iColumn ); |
734 |
|
735 |
/* Cache calculation for unique columns */ |
736 |
bUnique = iColspan === 1 ? true : false; |
737 |
|
738 |
/* If there is col / rowspan, copy the information into the layout grid */ |
739 |
for ( l=0 ; l<iColspan ; l++ ) |
740 |
{ |
741 |
for ( k=0 ; k<iRowspan ; k++ ) |
742 |
{ |
743 |
aLayout[i+k][iColShifted+l] = { |
744 |
"cell": nCell, |
745 |
"unique": bUnique |
746 |
}; |
747 |
aLayout[i+k].nTr = nTr; |
748 |
} |
749 |
} |
750 |
} |
751 |
nCell = nCell.nextSibling; |
752 |
} |
753 |
} |
754 |
} |
755 |
|
756 |
|
757 |
/** |
758 |
* Get an array of unique th elements, one for each column |
759 |
* @param {object} oSettings dataTables settings object |
760 |
* @param {node} nHeader automatically detect the layout from this node - optional |
761 |
* @param {array} aLayout thead/tfoot layout from _fnDetectHeader - optional |
762 |
* @returns array {node} aReturn list of unique th's |
763 |
* @memberof DataTable#oApi |
764 |
*/ |
765 |
function _fnGetUniqueThs ( oSettings, nHeader, aLayout ) |
766 |
{ |
767 |
var aReturn = []; |
768 |
if ( !aLayout ) |
769 |
{ |
770 |
aLayout = oSettings.aoHeader; |
771 |
if ( nHeader ) |
772 |
{ |
773 |
aLayout = []; |
774 |
_fnDetectHeader( aLayout, nHeader ); |
775 |
} |
776 |
} |
777 |
|
778 |
for ( var i=0, iLen=aLayout.length ; i<iLen ; i++ ) |
779 |
{ |
780 |
for ( var j=0, jLen=aLayout[i].length ; j<jLen ; j++ ) |
781 |
{ |
782 |
if ( aLayout[i][j].unique && |
783 |
(!aReturn[j] || !oSettings.bSortCellsTop) ) |
784 |
{ |
785 |
aReturn[j] = aLayout[i][j].cell; |
786 |
} |
787 |
} |
788 |
} |
789 |
|
790 |
return aReturn; |
791 |
} |