Project

General

Profile

Download (16.8 KB) Statistics
| Branch: | Tag: | Revision:
1
/**
2
 * Add a data array to the table, creating DOM node etc. This is the parallel to 
3
 * _fnGatherData, but for adding rows from a Javascript source, rather than a
4
 * DOM source.
5
 *  @param {object} oSettings dataTables settings object
6
 *  @param {array} aData data array to be added
7
 *  @returns {int} >=0 if successful (index of new aoData entry), -1 if failed
8
 *  @memberof DataTable#oApi
9
 */
10
function _fnAddData ( oSettings, aDataSupplied )
11
{
12
	var oCol;
13
	
14
	/* Take an independent copy of the data source so we can bash it about as we wish */
15
	var aDataIn = ($.isArray(aDataSupplied)) ?
16
		aDataSupplied.slice() :
17
		$.extend( true, {}, aDataSupplied );
18
	
19
	/* Create the object for storing information about this new row */
20
	var iRow = oSettings.aoData.length;
21
	var oData = $.extend( true, {}, DataTable.models.oRow );
22
	oData._aData = aDataIn;
23
	oSettings.aoData.push( oData );
24

    
25
	/* Create the cells */
26
	var nTd, sThisType;
27
	for ( var i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
28
	{
29
		oCol = oSettings.aoColumns[i];
30

    
31
		/* Use rendered data for filtering / sorting */
32
		if ( typeof oCol.fnRender === 'function' && oCol.bUseRendered && oCol.mData !== null )
33
		{
34
			_fnSetCellData( oSettings, iRow, i, _fnRender(oSettings, iRow, i) );
35
		}
36
		else
37
		{
38
			_fnSetCellData( oSettings, iRow, i, _fnGetCellData( oSettings, iRow, i ) );
39
		}
40
		
41
		/* See if we should auto-detect the column type */
42
		if ( oCol._bAutoType && oCol.sType != 'string' )
43
		{
44
			/* Attempt to auto detect the type - same as _fnGatherData() */
45
			var sVarType = _fnGetCellData( oSettings, iRow, i, 'type' );
46
			if ( sVarType !== null && sVarType !== '' )
47
			{
48
				sThisType = _fnDetectType( sVarType );
49
				if ( oCol.sType === null )
50
				{
51
					oCol.sType = sThisType;
52
				}
53
				else if ( oCol.sType != sThisType && oCol.sType != "html" )
54
				{
55
					/* String is always the 'fallback' option */
56
					oCol.sType = 'string';
57
				}
58
			}
59
		}
60
	}
61
	
62
	/* Add to the display array */
63
	oSettings.aiDisplayMaster.push( iRow );
64

    
65
	/* Create the DOM information */
66
	if ( !oSettings.oFeatures.bDeferRender )
67
	{
68
		_fnCreateTr( oSettings, iRow );
69
	}
70

    
71
	return iRow;
72
}
73

    
74

    
75
/**
76
 * Read in the data from the target table from the DOM
77
 *  @param {object} oSettings dataTables settings object
78
 *  @memberof DataTable#oApi
79
 */
80
function _fnGatherData( oSettings )
81
{
82
	var iLoop, i, iLen, j, jLen, jInner,
83
	 	nTds, nTrs, nTd, nTr, aLocalData, iThisIndex,
84
		iRow, iRows, iColumn, iColumns, sNodeName,
85
		oCol, oData;
86
	
87
	/*
88
	 * Process by row first
89
	 * Add the data object for the whole table - storing the tr node. Note - no point in getting
90
	 * DOM based data if we are going to go and replace it with Ajax source data.
91
	 */
92
	if ( oSettings.bDeferLoading || oSettings.sAjaxSource === null )
93
	{
94
		nTr = oSettings.nTBody.firstChild;
95
		while ( nTr )
96
		{
97
			if ( nTr.nodeName.toUpperCase() == "TR" )
98
			{
99
				iThisIndex = oSettings.aoData.length;
100
				nTr._DT_RowIndex = iThisIndex;
101
				oSettings.aoData.push( $.extend( true, {}, DataTable.models.oRow, {
102
					"nTr": nTr
103
				} ) );
104

    
105
				oSettings.aiDisplayMaster.push( iThisIndex );
106
				nTd = nTr.firstChild;
107
				jInner = 0;
108
				while ( nTd )
109
				{
110
					sNodeName = nTd.nodeName.toUpperCase();
111
					if ( sNodeName == "TD" || sNodeName == "TH" )
112
					{
113
						_fnSetCellData( oSettings, iThisIndex, jInner, $.trim(nTd.innerHTML) );
114
						jInner++;
115
					}
116
					nTd = nTd.nextSibling;
117
				}
118
			}
119
			nTr = nTr.nextSibling;
120
		}
121
	}
122
	
123
	/* Gather in the TD elements of the Table - note that this is basically the same as
124
	 * fnGetTdNodes, but that function takes account of hidden columns, which we haven't yet
125
	 * setup!
126
	 */
127
	nTrs = _fnGetTrNodes( oSettings );
128
	nTds = [];
129
	for ( i=0, iLen=nTrs.length ; i<iLen ; i++ )
130
	{
131
		nTd = nTrs[i].firstChild;
132
		while ( nTd )
133
		{
134
			sNodeName = nTd.nodeName.toUpperCase();
135
			if ( sNodeName == "TD" || sNodeName == "TH" )
136
			{
137
				nTds.push( nTd );
138
			}
139
			nTd = nTd.nextSibling;
140
		}
141
	}
142
	
143
	/* Now process by column */
144
	for ( iColumn=0, iColumns=oSettings.aoColumns.length ; iColumn<iColumns ; iColumn++ )
145
	{
146
		oCol = oSettings.aoColumns[iColumn];
147

    
148
		/* Get the title of the column - unless there is a user set one */
149
		if ( oCol.sTitle === null )
150
		{
151
			oCol.sTitle = oCol.nTh.innerHTML;
152
		}
153
		
154
		var
155
			bAutoType = oCol._bAutoType,
156
			bRender = typeof oCol.fnRender === 'function',
157
			bClass = oCol.sClass !== null,
158
			bVisible = oCol.bVisible,
159
			nCell, sThisType, sRendered, sValType;
160
		
161
		/* A single loop to rule them all (and be more efficient) */
162
		if ( bAutoType || bRender || bClass || !bVisible )
163
		{
164
			for ( iRow=0, iRows=oSettings.aoData.length ; iRow<iRows ; iRow++ )
165
			{
166
				oData = oSettings.aoData[iRow];
167
				nCell = nTds[ (iRow*iColumns) + iColumn ];
168
				
169
				/* Type detection */
170
				if ( bAutoType && oCol.sType != 'string' )
171
				{
172
					sValType = _fnGetCellData( oSettings, iRow, iColumn, 'type' );
173
					if ( sValType !== '' )
174
					{
175
						sThisType = _fnDetectType( sValType );
176
						if ( oCol.sType === null )
177
						{
178
							oCol.sType = sThisType;
179
						}
180
						else if ( oCol.sType != sThisType && 
181
						          oCol.sType != "html" )
182
						{
183
							/* String is always the 'fallback' option */
184
							oCol.sType = 'string';
185
						}
186
					}
187
				}
188

    
189
				if ( oCol.mRender )
190
				{
191
					// mRender has been defined, so we need to get the value and set it
192
					nCell.innerHTML = _fnGetCellData( oSettings, iRow, iColumn, 'display' );
193
				}
194
				else if ( oCol.mData !== iColumn )
195
				{
196
					// If mData is not the same as the column number, then we need to
197
					// get the dev set value. If it is the column, no point in wasting
198
					// time setting the value that is already there!
199
					nCell.innerHTML = _fnGetCellData( oSettings, iRow, iColumn, 'display' );
200
				}
201
				
202
				/* Rendering */
203
				if ( bRender )
204
				{
205
					sRendered = _fnRender( oSettings, iRow, iColumn );
206
					nCell.innerHTML = sRendered;
207
					if ( oCol.bUseRendered )
208
					{
209
						/* Use the rendered data for filtering / sorting */
210
						_fnSetCellData( oSettings, iRow, iColumn, sRendered );
211
					}
212
				}
213
				
214
				/* Classes */
215
				if ( bClass )
216
				{
217
					nCell.className += ' '+oCol.sClass;
218
				}
219
				
220
				/* Column visibility */
221
				if ( !bVisible )
222
				{
223
					oData._anHidden[iColumn] = nCell;
224
					nCell.parentNode.removeChild( nCell );
225
				}
226
				else
227
				{
228
					oData._anHidden[iColumn] = null;
229
				}
230

    
231
				if ( oCol.fnCreatedCell )
232
				{
233
					oCol.fnCreatedCell.call( oSettings.oInstance,
234
						nCell, _fnGetCellData( oSettings, iRow, iColumn, 'display' ), oData._aData, iRow, iColumn
235
					);
236
				}
237
			}
238
		}
239
	}
240

    
241
	/* Row created callbacks */
242
	if ( oSettings.aoRowCreatedCallback.length !== 0 )
243
	{
244
		for ( i=0, iLen=oSettings.aoData.length ; i<iLen ; i++ )
245
		{
246
			oData = oSettings.aoData[i];
247
			_fnCallbackFire( oSettings, 'aoRowCreatedCallback', null, [oData.nTr, oData._aData, i] );
248
		}
249
	}
250
}
251

    
252

    
253
/**
254
 * Take a TR element and convert it to an index in aoData
255
 *  @param {object} oSettings dataTables settings object
256
 *  @param {node} n the TR element to find
257
 *  @returns {int} index if the node is found, null if not
258
 *  @memberof DataTable#oApi
259
 */
260
function _fnNodeToDataIndex( oSettings, n )
261
{
262
	return (n._DT_RowIndex!==undefined) ? n._DT_RowIndex : null;
263
}
264

    
265

    
266
/**
267
 * Take a TD element and convert it into a column data index (not the visible index)
268
 *  @param {object} oSettings dataTables settings object
269
 *  @param {int} iRow The row number the TD/TH can be found in
270
 *  @param {node} n The TD/TH element to find
271
 *  @returns {int} index if the node is found, -1 if not
272
 *  @memberof DataTable#oApi
273
 */
274
function _fnNodeToColumnIndex( oSettings, iRow, n )
275
{
276
	var anCells = _fnGetTdNodes( oSettings, iRow );
277

    
278
	for ( var i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
279
	{
280
		if ( anCells[i] === n )
281
		{
282
			return i;
283
		}
284
	}
285
	return -1;
286
}
287

    
288

    
289
/**
290
 * Get an array of data for a given row from the internal data cache
291
 *  @param {object} oSettings dataTables settings object
292
 *  @param {int} iRow aoData row id
293
 *  @param {string} sSpecific data get type ('type' 'filter' 'sort')
294
 *  @param {array} aiColumns Array of column indexes to get data from
295
 *  @returns {array} Data array
296
 *  @memberof DataTable#oApi
297
 */
298
function _fnGetRowData( oSettings, iRow, sSpecific, aiColumns )
299
{
300
	var out = [];
301
	for ( var i=0, iLen=aiColumns.length ; i<iLen ; i++ )
302
	{
303
		out.push( _fnGetCellData( oSettings, iRow, aiColumns[i], sSpecific ) );
304
	}
305
	return out;
306
}
307

    
308

    
309
/**
310
 * Get the data for a given cell from the internal cache, taking into account data mapping
311
 *  @param {object} oSettings dataTables settings object
312
 *  @param {int} iRow aoData row id
313
 *  @param {int} iCol Column index
314
 *  @param {string} sSpecific data get type ('display', 'type' 'filter' 'sort')
315
 *  @returns {*} Cell data
316
 *  @memberof DataTable#oApi
317
 */
318
function _fnGetCellData( oSettings, iRow, iCol, sSpecific )
319
{
320
	var sData;
321
	var oCol = oSettings.aoColumns[iCol];
322
	var oData = oSettings.aoData[iRow]._aData;
323

    
324
	if ( (sData=oCol.fnGetData( oData, sSpecific )) === undefined )
325
	{
326
		if ( oSettings.iDrawError != oSettings.iDraw && oCol.sDefaultContent === null )
327
		{
328
			_fnLog( oSettings, 0, "Requested unknown parameter "+
329
				(typeof oCol.mData=='function' ? '{mData function}' : "'"+oCol.mData+"'")+
330
				" from the data source for row "+iRow );
331
			oSettings.iDrawError = oSettings.iDraw;
332
		}
333
		return oCol.sDefaultContent;
334
	}
335

    
336
	/* When the data source is null, we can use default column data */
337
	if ( sData === null && oCol.sDefaultContent !== null )
338
	{
339
		sData = oCol.sDefaultContent;
340
	}
341
	else if ( typeof sData === 'function' )
342
	{
343
		/* If the data source is a function, then we run it and use the return */
344
		return sData();
345
	}
346

    
347
	if ( sSpecific == 'display' && sData === null )
348
	{
349
		return '';
350
	}
351
	return sData;
352
}
353

    
354

    
355
/**
356
 * Set the value for a specific cell, into the internal data cache
357
 *  @param {object} oSettings dataTables settings object
358
 *  @param {int} iRow aoData row id
359
 *  @param {int} iCol Column index
360
 *  @param {*} val Value to set
361
 *  @memberof DataTable#oApi
362
 */
363
function _fnSetCellData( oSettings, iRow, iCol, val )
364
{
365
	var oCol = oSettings.aoColumns[iCol];
366
	var oData = oSettings.aoData[iRow]._aData;
367

    
368
	oCol.fnSetData( oData, val );
369
}
370

    
371

    
372
// Private variable that is used to match array syntax in the data property object
373
var __reArray = /\[.*?\]$/;
374

    
375
/**
376
 * Return a function that can be used to get data from a source object, taking
377
 * into account the ability to use nested objects as a source
378
 *  @param {string|int|function} mSource The data source for the object
379
 *  @returns {function} Data get function
380
 *  @memberof DataTable#oApi
381
 */
382
function _fnGetObjectDataFn( mSource )
383
{
384
	if ( mSource === null )
385
	{
386
		/* Give an empty string for rendering / sorting etc */
387
		return function (data, type) {
388
			return null;
389
		};
390
	}
391
	else if ( typeof mSource === 'function' )
392
	{
393
		return function (data, type, extra) {
394
			return mSource( data, type, extra );
395
		};
396
	}
397
	else if ( typeof mSource === 'string' && (mSource.indexOf('.') !== -1 || mSource.indexOf('[') !== -1) )
398
	{
399
		/* If there is a . in the source string then the data source is in a 
400
		 * nested object so we loop over the data for each level to get the next
401
		 * level down. On each loop we test for undefined, and if found immediately
402
		 * return. This allows entire objects to be missing and sDefaultContent to
403
		 * be used if defined, rather than throwing an error
404
		 */
405
		var fetchData = function (data, type, src) {
406
			var a = src.split('.');
407
			var arrayNotation, out, innerSrc;
408

    
409
			if ( src !== "" )
410
			{
411
				for ( var i=0, iLen=a.length ; i<iLen ; i++ )
412
				{
413
					// Check if we are dealing with an array notation request
414
					arrayNotation = a[i].match(__reArray);
415

    
416
					if ( arrayNotation ) {
417
						a[i] = a[i].replace(__reArray, '');
418

    
419
						// Condition allows simply [] to be passed in
420
						if ( a[i] !== "" ) {
421
							data = data[ a[i] ];
422
						}
423
						out = [];
424
						
425
						// Get the remainder of the nested object to get
426
						a.splice( 0, i+1 );
427
						innerSrc = a.join('.');
428

    
429
						// Traverse each entry in the array getting the properties requested
430
						for ( var j=0, jLen=data.length ; j<jLen ; j++ ) {
431
							out.push( fetchData( data[j], type, innerSrc ) );
432
						}
433

    
434
						// If a string is given in between the array notation indicators, that
435
						// is used to join the strings together, otherwise an array is returned
436
						var join = arrayNotation[0].substring(1, arrayNotation[0].length-1);
437
						data = (join==="") ? out : out.join(join);
438

    
439
						// The inner call to fetchData has already traversed through the remainder
440
						// of the source requested, so we exit from the loop
441
						break;
442
					}
443

    
444
					if ( data === null || data[ a[i] ] === undefined )
445
					{
446
						return undefined;
447
					}
448
					data = data[ a[i] ];
449
				}
450
			}
451

    
452
			return data;
453
		};
454

    
455
		return function (data, type) {
456
			return fetchData( data, type, mSource );
457
		};
458
	}
459
	else
460
	{
461
		/* Array or flat object mapping */
462
		return function (data, type) {
463
			return data[mSource];	
464
		};
465
	}
466
}
467

    
468

    
469
/**
470
 * Return a function that can be used to set data from a source object, taking
471
 * into account the ability to use nested objects as a source
472
 *  @param {string|int|function} mSource The data source for the object
473
 *  @returns {function} Data set function
474
 *  @memberof DataTable#oApi
475
 */
476
function _fnSetObjectDataFn( mSource )
477
{
478
	if ( mSource === null )
479
	{
480
		/* Nothing to do when the data source is null */
481
		return function (data, val) {};
482
	}
483
	else if ( typeof mSource === 'function' )
484
	{
485
		return function (data, val) {
486
			mSource( data, 'set', val );
487
		};
488
	}
489
	else if ( typeof mSource === 'string' && (mSource.indexOf('.') !== -1 || mSource.indexOf('[') !== -1) )
490
	{
491
		/* Like the get, we need to get data from a nested object */
492
		var setData = function (data, val, src) {
493
			var a = src.split('.'), b;
494
			var arrayNotation, o, innerSrc;
495

    
496
			for ( var i=0, iLen=a.length-1 ; i<iLen ; i++ )
497
			{
498
				// Check if we are dealing with an array notation request
499
				arrayNotation = a[i].match(__reArray);
500

    
501
				if ( arrayNotation )
502
				{
503
					a[i] = a[i].replace(__reArray, '');
504
					data[ a[i] ] = [];
505
					
506
					// Get the remainder of the nested object to set so we can recurse
507
					b = a.slice();
508
					b.splice( 0, i+1 );
509
					innerSrc = b.join('.');
510

    
511
					// Traverse each entry in the array setting the properties requested
512
					for ( var j=0, jLen=val.length ; j<jLen ; j++ )
513
					{
514
						o = {};
515
						setData( o, val[j], innerSrc );
516
						data[ a[i] ].push( o );
517
					}
518

    
519
					// The inner call to setData has already traversed through the remainder
520
					// of the source and has set the data, thus we can exit here
521
					return;
522
				}
523

    
524
				// If the nested object doesn't currently exist - since we are
525
				// trying to set the value - create it
526
				if ( data[ a[i] ] === null || data[ a[i] ] === undefined )
527
				{
528
					data[ a[i] ] = {};
529
				}
530
				data = data[ a[i] ];
531
			}
532

    
533
			// If array notation is used, we just want to strip it and use the property name
534
			// and assign the value. If it isn't used, then we get the result we want anyway
535
			data[ a[a.length-1].replace(__reArray, '') ] = val;
536
		};
537

    
538
		return function (data, val) {
539
			return setData( data, val, mSource );
540
		};
541
	}
542
	else
543
	{
544
		/* Array or flat object mapping */
545
		return function (data, val) {
546
			data[mSource] = val;	
547
		};
548
	}
549
}
550

    
551

    
552
/**
553
 * Return an array with the full table data
554
 *  @param {object} oSettings dataTables settings object
555
 *  @returns array {array} aData Master data array
556
 *  @memberof DataTable#oApi
557
 */
558
function _fnGetDataMaster ( oSettings )
559
{
560
	var aData = [];
561
	var iLen = oSettings.aoData.length;
562
	for ( var i=0 ; i<iLen; i++ )
563
	{
564
		aData.push( oSettings.aoData[i]._aData );
565
	}
566
	return aData;
567
}
568

    
569

    
570
/**
571
 * Nuke the table
572
 *  @param {object} oSettings dataTables settings object
573
 *  @memberof DataTable#oApi
574
 */
575
function _fnClearTable( oSettings )
576
{
577
	oSettings.aoData.splice( 0, oSettings.aoData.length );
578
	oSettings.aiDisplayMaster.splice( 0, oSettings.aiDisplayMaster.length );
579
	oSettings.aiDisplay.splice( 0, oSettings.aiDisplay.length );
580
	_fnCalculateEnd( oSettings );
581
}
582

    
583

    
584
 /**
585
 * Take an array of integers (index array) and remove a target integer (value - not 
586
 * the key!)
587
 *  @param {array} a Index array to target
588
 *  @param {int} iTarget value to find
589
 *  @memberof DataTable#oApi
590
 */
591
function _fnDeleteIndex( a, iTarget )
592
{
593
	var iTargetIndex = -1;
594
	
595
	for ( var i=0, iLen=a.length ; i<iLen ; i++ )
596
	{
597
		if ( a[i] == iTarget )
598
		{
599
			iTargetIndex = i;
600
		}
601
		else if ( a[i] > iTarget )
602
		{
603
			a[i]--;
604
		}
605
	}
606
	
607
	if ( iTargetIndex != -1 )
608
	{
609
		a.splice( iTargetIndex, 1 );
610
	}
611
}
612

    
613

    
614
 /**
615
 * Call the developer defined fnRender function for a given cell (row/column) with
616
 * the required parameters and return the result.
617
 *  @param {object} oSettings dataTables settings object
618
 *  @param {int} iRow aoData index for the row
619
 *  @param {int} iCol aoColumns index for the column
620
 *  @returns {*} Return of the developer's fnRender function
621
 *  @memberof DataTable#oApi
622
 */
623
function _fnRender( oSettings, iRow, iCol )
624
{
625
	var oCol = oSettings.aoColumns[iCol];
626

    
627
	return oCol.fnRender( {
628
		"iDataRow":    iRow,
629
		"iDataColumn": iCol,
630
		"oSettings":   oSettings,
631
		"aData":       oSettings.aoData[iRow]._aData,
632
		"mDataProp":   oCol.mData
633
	}, _fnGetCellData(oSettings, iRow, iCol, 'display') );
634
}
(4-4/16)