0
|
1 var i=0, iLen, j, jLen, k, kLen;
|
|
2 var sId = this.getAttribute( 'id' );
|
|
3 var bInitHandedOff = false;
|
|
4 var bUsePassedData = false;
|
|
5
|
|
6
|
|
7 /* Sanity check */
|
|
8 if ( this.nodeName.toLowerCase() != 'table' )
|
|
9 {
|
|
10 _fnLog( null, 0, "Attempted to initialise DataTables on a node which is not a "+
|
|
11 "table: "+this.nodeName );
|
|
12 return;
|
|
13 }
|
|
14
|
|
15 /* Check to see if we are re-initialising a table */
|
|
16 for ( i=0, iLen=DataTable.settings.length ; i<iLen ; i++ )
|
|
17 {
|
|
18 /* Base check on table node */
|
|
19 if ( DataTable.settings[i].nTable == this )
|
|
20 {
|
|
21 if ( oInit === undefined || oInit.bRetrieve )
|
|
22 {
|
|
23 return DataTable.settings[i].oInstance;
|
|
24 }
|
|
25 else if ( oInit.bDestroy )
|
|
26 {
|
|
27 DataTable.settings[i].oInstance.fnDestroy();
|
|
28 break;
|
|
29 }
|
|
30 else
|
|
31 {
|
|
32 _fnLog( DataTable.settings[i], 0, "Cannot reinitialise DataTable.\n\n"+
|
|
33 "To retrieve the DataTables object for this table, pass no arguments or see "+
|
|
34 "the docs for bRetrieve and bDestroy" );
|
|
35 return;
|
|
36 }
|
|
37 }
|
|
38
|
|
39 /* If the element we are initialising has the same ID as a table which was previously
|
|
40 * initialised, but the table nodes don't match (from before) then we destroy the old
|
|
41 * instance by simply deleting it. This is under the assumption that the table has been
|
|
42 * destroyed by other methods. Anyone using non-id selectors will need to do this manually
|
|
43 */
|
|
44 if ( DataTable.settings[i].sTableId == this.id )
|
|
45 {
|
|
46 DataTable.settings.splice( i, 1 );
|
|
47 break;
|
|
48 }
|
|
49 }
|
|
50
|
|
51 /* Ensure the table has an ID - required for accessibility */
|
|
52 if ( sId === null || sId === "" )
|
|
53 {
|
|
54 sId = "DataTables_Table_"+(DataTable.ext._oExternConfig.iNextUnique++);
|
|
55 this.id = sId;
|
|
56 }
|
|
57
|
|
58 /* Create the settings object for this table and set some of the default parameters */
|
|
59 var oSettings = $.extend( true, {}, DataTable.models.oSettings, {
|
|
60 "nTable": this,
|
|
61 "oApi": _that.oApi,
|
|
62 "oInit": oInit,
|
|
63 "sDestroyWidth": $(this).width(),
|
|
64 "sInstance": sId,
|
|
65 "sTableId": sId
|
|
66 } );
|
|
67 DataTable.settings.push( oSettings );
|
|
68
|
|
69 // Need to add the instance after the instance after the settings object has been added
|
|
70 // to the settings array, so we can self reference the table instance if more than one
|
|
71 oSettings.oInstance = (_that.length===1) ? _that : $(this).dataTable();
|
|
72
|
|
73 /* Setting up the initialisation object */
|
|
74 if ( !oInit )
|
|
75 {
|
|
76 oInit = {};
|
|
77 }
|
|
78
|
|
79 // Backwards compatibility, before we apply all the defaults
|
|
80 if ( oInit.oLanguage )
|
|
81 {
|
|
82 _fnLanguageCompat( oInit.oLanguage );
|
|
83 }
|
|
84
|
|
85 oInit = _fnExtend( $.extend(true, {}, DataTable.defaults), oInit );
|
|
86
|
|
87 // Map the initialisation options onto the settings object
|
|
88 _fnMap( oSettings.oFeatures, oInit, "bPaginate" );
|
|
89 _fnMap( oSettings.oFeatures, oInit, "bLengthChange" );
|
|
90 _fnMap( oSettings.oFeatures, oInit, "bFilter" );
|
|
91 _fnMap( oSettings.oFeatures, oInit, "bSort" );
|
|
92 _fnMap( oSettings.oFeatures, oInit, "bInfo" );
|
|
93 _fnMap( oSettings.oFeatures, oInit, "bProcessing" );
|
|
94 _fnMap( oSettings.oFeatures, oInit, "bAutoWidth" );
|
|
95 _fnMap( oSettings.oFeatures, oInit, "bSortClasses" );
|
|
96 _fnMap( oSettings.oFeatures, oInit, "bServerSide" );
|
|
97 _fnMap( oSettings.oFeatures, oInit, "bDeferRender" );
|
|
98 _fnMap( oSettings.oScroll, oInit, "sScrollX", "sX" );
|
|
99 _fnMap( oSettings.oScroll, oInit, "sScrollXInner", "sXInner" );
|
|
100 _fnMap( oSettings.oScroll, oInit, "sScrollY", "sY" );
|
|
101 _fnMap( oSettings.oScroll, oInit, "bScrollCollapse", "bCollapse" );
|
|
102 _fnMap( oSettings.oScroll, oInit, "bScrollInfinite", "bInfinite" );
|
|
103 _fnMap( oSettings.oScroll, oInit, "iScrollLoadGap", "iLoadGap" );
|
|
104 _fnMap( oSettings.oScroll, oInit, "bScrollAutoCss", "bAutoCss" );
|
|
105 _fnMap( oSettings, oInit, "asStripeClasses" );
|
|
106 _fnMap( oSettings, oInit, "asStripClasses", "asStripeClasses" ); // legacy
|
|
107 _fnMap( oSettings, oInit, "fnServerData" );
|
|
108 _fnMap( oSettings, oInit, "fnFormatNumber" );
|
|
109 _fnMap( oSettings, oInit, "sServerMethod" );
|
|
110 _fnMap( oSettings, oInit, "aaSorting" );
|
|
111 _fnMap( oSettings, oInit, "aaSortingFixed" );
|
|
112 _fnMap( oSettings, oInit, "aLengthMenu" );
|
|
113 _fnMap( oSettings, oInit, "sPaginationType" );
|
|
114 _fnMap( oSettings, oInit, "sAjaxSource" );
|
|
115 _fnMap( oSettings, oInit, "sAjaxDataProp" );
|
|
116 _fnMap( oSettings, oInit, "iCookieDuration" );
|
|
117 _fnMap( oSettings, oInit, "sCookiePrefix" );
|
|
118 _fnMap( oSettings, oInit, "sDom" );
|
|
119 _fnMap( oSettings, oInit, "bSortCellsTop" );
|
|
120 _fnMap( oSettings, oInit, "iTabIndex" );
|
|
121 _fnMap( oSettings, oInit, "oSearch", "oPreviousSearch" );
|
|
122 _fnMap( oSettings, oInit, "aoSearchCols", "aoPreSearchCols" );
|
|
123 _fnMap( oSettings, oInit, "iDisplayLength", "_iDisplayLength" );
|
|
124 _fnMap( oSettings, oInit, "bJQueryUI", "bJUI" );
|
|
125 _fnMap( oSettings, oInit, "fnCookieCallback" );
|
|
126 _fnMap( oSettings, oInit, "fnStateLoad" );
|
|
127 _fnMap( oSettings, oInit, "fnStateSave" );
|
|
128 _fnMap( oSettings.oLanguage, oInit, "fnInfoCallback" );
|
|
129
|
|
130 /* Callback functions which are array driven */
|
|
131 _fnCallbackReg( oSettings, 'aoDrawCallback', oInit.fnDrawCallback, 'user' );
|
|
132 _fnCallbackReg( oSettings, 'aoServerParams', oInit.fnServerParams, 'user' );
|
|
133 _fnCallbackReg( oSettings, 'aoStateSaveParams', oInit.fnStateSaveParams, 'user' );
|
|
134 _fnCallbackReg( oSettings, 'aoStateLoadParams', oInit.fnStateLoadParams, 'user' );
|
|
135 _fnCallbackReg( oSettings, 'aoStateLoaded', oInit.fnStateLoaded, 'user' );
|
|
136 _fnCallbackReg( oSettings, 'aoRowCallback', oInit.fnRowCallback, 'user' );
|
|
137 _fnCallbackReg( oSettings, 'aoRowCreatedCallback', oInit.fnCreatedRow, 'user' );
|
|
138 _fnCallbackReg( oSettings, 'aoHeaderCallback', oInit.fnHeaderCallback, 'user' );
|
|
139 _fnCallbackReg( oSettings, 'aoFooterCallback', oInit.fnFooterCallback, 'user' );
|
|
140 _fnCallbackReg( oSettings, 'aoInitComplete', oInit.fnInitComplete, 'user' );
|
|
141 _fnCallbackReg( oSettings, 'aoPreDrawCallback', oInit.fnPreDrawCallback, 'user' );
|
|
142
|
|
143 if ( oSettings.oFeatures.bServerSide && oSettings.oFeatures.bSort &&
|
|
144 oSettings.oFeatures.bSortClasses )
|
|
145 {
|
|
146 /* Enable sort classes for server-side processing. Safe to do it here, since server-side
|
|
147 * processing must be enabled by the developer
|
|
148 */
|
|
149 _fnCallbackReg( oSettings, 'aoDrawCallback', _fnSortingClasses, 'server_side_sort_classes' );
|
|
150 }
|
|
151 else if ( oSettings.oFeatures.bDeferRender )
|
|
152 {
|
|
153 _fnCallbackReg( oSettings, 'aoDrawCallback', _fnSortingClasses, 'defer_sort_classes' );
|
|
154 }
|
|
155
|
|
156 if ( oInit.bJQueryUI )
|
|
157 {
|
|
158 /* Use the JUI classes object for display. You could clone the oStdClasses object if
|
|
159 * you want to have multiple tables with multiple independent classes
|
|
160 */
|
|
161 $.extend( oSettings.oClasses, DataTable.ext.oJUIClasses );
|
|
162
|
|
163 if ( oInit.sDom === DataTable.defaults.sDom && DataTable.defaults.sDom === "lfrtip" )
|
|
164 {
|
|
165 /* Set the DOM to use a layout suitable for jQuery UI's theming */
|
|
166 oSettings.sDom = '<"H"lfr>t<"F"ip>';
|
|
167 }
|
|
168 }
|
|
169 else
|
|
170 {
|
|
171 $.extend( oSettings.oClasses, DataTable.ext.oStdClasses );
|
|
172 }
|
|
173 $(this).addClass( oSettings.oClasses.sTable );
|
|
174
|
|
175 /* Calculate the scroll bar width and cache it for use later on */
|
|
176 if ( oSettings.oScroll.sX !== "" || oSettings.oScroll.sY !== "" )
|
|
177 {
|
|
178 oSettings.oScroll.iBarWidth = _fnScrollBarWidth();
|
|
179 }
|
|
180
|
|
181 if ( oSettings.iInitDisplayStart === undefined )
|
|
182 {
|
|
183 /* Display start point, taking into account the save saving */
|
|
184 oSettings.iInitDisplayStart = oInit.iDisplayStart;
|
|
185 oSettings._iDisplayStart = oInit.iDisplayStart;
|
|
186 }
|
|
187
|
|
188 /* Must be done after everything which can be overridden by a cookie! */
|
|
189 if ( oInit.bStateSave )
|
|
190 {
|
|
191 oSettings.oFeatures.bStateSave = true;
|
|
192 _fnLoadState( oSettings, oInit );
|
|
193 _fnCallbackReg( oSettings, 'aoDrawCallback', _fnSaveState, 'state_save' );
|
|
194 }
|
|
195
|
|
196 if ( oInit.iDeferLoading !== null )
|
|
197 {
|
|
198 oSettings.bDeferLoading = true;
|
|
199 var tmp = $.isArray( oInit.iDeferLoading );
|
|
200 oSettings._iRecordsDisplay = tmp ? oInit.iDeferLoading[0] : oInit.iDeferLoading;
|
|
201 oSettings._iRecordsTotal = tmp ? oInit.iDeferLoading[1] : oInit.iDeferLoading;
|
|
202 }
|
|
203
|
|
204 if ( oInit.aaData !== null )
|
|
205 {
|
|
206 bUsePassedData = true;
|
|
207 }
|
|
208
|
|
209 /* Language definitions */
|
|
210 if ( oInit.oLanguage.sUrl !== "" )
|
|
211 {
|
|
212 /* Get the language definitions from a file - because this Ajax call makes the language
|
|
213 * get async to the remainder of this function we use bInitHandedOff to indicate that
|
|
214 * _fnInitialise will be fired by the returned Ajax handler, rather than the constructor
|
|
215 */
|
|
216 oSettings.oLanguage.sUrl = oInit.oLanguage.sUrl;
|
|
217 $.getJSON( oSettings.oLanguage.sUrl, null, function( json ) {
|
|
218 _fnLanguageCompat( json );
|
|
219 $.extend( true, oSettings.oLanguage, oInit.oLanguage, json );
|
|
220 _fnInitialise( oSettings );
|
|
221 } );
|
|
222 bInitHandedOff = true;
|
|
223 }
|
|
224 else
|
|
225 {
|
|
226 $.extend( true, oSettings.oLanguage, oInit.oLanguage );
|
|
227 }
|
|
228
|
|
229
|
|
230 /*
|
|
231 * Stripes
|
|
232 */
|
|
233 if ( oInit.asStripeClasses === null )
|
|
234 {
|
|
235 oSettings.asStripeClasses =[
|
|
236 oSettings.oClasses.sStripeOdd,
|
|
237 oSettings.oClasses.sStripeEven
|
|
238 ];
|
|
239 }
|
|
240
|
|
241 /* Remove row stripe classes if they are already on the table row */
|
|
242 iLen=oSettings.asStripeClasses.length;
|
|
243 oSettings.asDestroyStripes = [];
|
|
244 if (iLen)
|
|
245 {
|
|
246 var bStripeRemove = false;
|
|
247 var anRows = $(this).children('tbody').children('tr:lt(' + iLen + ')');
|
|
248 for ( i=0 ; i<iLen ; i++ )
|
|
249 {
|
|
250 if ( anRows.hasClass( oSettings.asStripeClasses[i] ) )
|
|
251 {
|
|
252 bStripeRemove = true;
|
|
253
|
|
254 /* Store the classes which we are about to remove so they can be re-added on destroy */
|
|
255 oSettings.asDestroyStripes.push( oSettings.asStripeClasses[i] );
|
|
256 }
|
|
257 }
|
|
258
|
|
259 if ( bStripeRemove )
|
|
260 {
|
|
261 anRows.removeClass( oSettings.asStripeClasses.join(' ') );
|
|
262 }
|
|
263 }
|
|
264
|
|
265 /*
|
|
266 * Columns
|
|
267 * See if we should load columns automatically or use defined ones
|
|
268 */
|
|
269 var anThs = [];
|
|
270 var aoColumnsInit;
|
|
271 var nThead = this.getElementsByTagName('thead');
|
|
272 if ( nThead.length !== 0 )
|
|
273 {
|
|
274 _fnDetectHeader( oSettings.aoHeader, nThead[0] );
|
|
275 anThs = _fnGetUniqueThs( oSettings );
|
|
276 }
|
|
277
|
|
278 /* If not given a column array, generate one with nulls */
|
|
279 if ( oInit.aoColumns === null )
|
|
280 {
|
|
281 aoColumnsInit = [];
|
|
282 for ( i=0, iLen=anThs.length ; i<iLen ; i++ )
|
|
283 {
|
|
284 aoColumnsInit.push( null );
|
|
285 }
|
|
286 }
|
|
287 else
|
|
288 {
|
|
289 aoColumnsInit = oInit.aoColumns;
|
|
290 }
|
|
291
|
|
292 /* Add the columns */
|
|
293 for ( i=0, iLen=aoColumnsInit.length ; i<iLen ; i++ )
|
|
294 {
|
|
295 /* Short cut - use the loop to check if we have column visibility state to restore */
|
|
296 if ( oInit.saved_aoColumns !== undefined && oInit.saved_aoColumns.length == iLen )
|
|
297 {
|
|
298 if ( aoColumnsInit[i] === null )
|
|
299 {
|
|
300 aoColumnsInit[i] = {};
|
|
301 }
|
|
302 aoColumnsInit[i].bVisible = oInit.saved_aoColumns[i].bVisible;
|
|
303 }
|
|
304
|
|
305 _fnAddColumn( oSettings, anThs ? anThs[i] : null );
|
|
306 }
|
|
307
|
|
308 /* Apply the column definitions */
|
|
309 _fnApplyColumnDefs( oSettings, oInit.aoColumnDefs, aoColumnsInit, function (iCol, oDef) {
|
|
310 _fnColumnOptions( oSettings, iCol, oDef );
|
|
311 } );
|
|
312
|
|
313
|
|
314 /*
|
|
315 * Sorting
|
|
316 * Check the aaSorting array
|
|
317 */
|
|
318 for ( i=0, iLen=oSettings.aaSorting.length ; i<iLen ; i++ )
|
|
319 {
|
|
320 if ( oSettings.aaSorting[i][0] >= oSettings.aoColumns.length )
|
|
321 {
|
|
322 oSettings.aaSorting[i][0] = 0;
|
|
323 }
|
|
324 var oColumn = oSettings.aoColumns[ oSettings.aaSorting[i][0] ];
|
|
325
|
|
326 /* Add a default sorting index */
|
|
327 if ( oSettings.aaSorting[i][2] === undefined )
|
|
328 {
|
|
329 oSettings.aaSorting[i][2] = 0;
|
|
330 }
|
|
331
|
|
332 /* If aaSorting is not defined, then we use the first indicator in asSorting */
|
|
333 if ( oInit.aaSorting === undefined && oSettings.saved_aaSorting === undefined )
|
|
334 {
|
|
335 oSettings.aaSorting[i][1] = oColumn.asSorting[0];
|
|
336 }
|
|
337
|
|
338 /* Set the current sorting index based on aoColumns.asSorting */
|
|
339 for ( j=0, jLen=oColumn.asSorting.length ; j<jLen ; j++ )
|
|
340 {
|
|
341 if ( oSettings.aaSorting[i][1] == oColumn.asSorting[j] )
|
|
342 {
|
|
343 oSettings.aaSorting[i][2] = j;
|
|
344 break;
|
|
345 }
|
|
346 }
|
|
347 }
|
|
348
|
|
349 /* Do a first pass on the sorting classes (allows any size changes to be taken into
|
|
350 * account, and also will apply sorting disabled classes if disabled
|
|
351 */
|
|
352 _fnSortingClasses( oSettings );
|
|
353
|
|
354
|
|
355 /*
|
|
356 * Final init
|
|
357 * Cache the header, body and footer as required, creating them if needed
|
|
358 */
|
|
359
|
|
360 /* Browser support detection */
|
|
361 _fnBrowserDetect( oSettings );
|
|
362
|
|
363 // Work around for Webkit bug 83867 - store the caption-side before removing from doc
|
|
364 var captions = $(this).children('caption').each( function () {
|
|
365 this._captionSide = $(this).css('caption-side');
|
|
366 } );
|
|
367
|
|
368 var thead = $(this).children('thead');
|
|
369 if ( thead.length === 0 )
|
|
370 {
|
|
371 thead = [ document.createElement( 'thead' ) ];
|
|
372 this.appendChild( thead[0] );
|
|
373 }
|
|
374 oSettings.nTHead = thead[0];
|
|
375
|
|
376 var tbody = $(this).children('tbody');
|
|
377 if ( tbody.length === 0 )
|
|
378 {
|
|
379 tbody = [ document.createElement( 'tbody' ) ];
|
|
380 this.appendChild( tbody[0] );
|
|
381 }
|
|
382 oSettings.nTBody = tbody[0];
|
|
383 oSettings.nTBody.setAttribute( "role", "alert" );
|
|
384 oSettings.nTBody.setAttribute( "aria-live", "polite" );
|
|
385 oSettings.nTBody.setAttribute( "aria-relevant", "all" );
|
|
386
|
|
387 var tfoot = $(this).children('tfoot');
|
|
388 if ( tfoot.length === 0 && captions.length > 0 && (oSettings.oScroll.sX !== "" || oSettings.oScroll.sY !== "") )
|
|
389 {
|
|
390 // If we are a scrolling table, and no footer has been given, then we need to create
|
|
391 // a tfoot element for the caption element to be appended to
|
|
392 tfoot = [ document.createElement( 'tfoot' ) ];
|
|
393 this.appendChild( tfoot[0] );
|
|
394 }
|
|
395
|
|
396 if ( tfoot.length > 0 )
|
|
397 {
|
|
398 oSettings.nTFoot = tfoot[0];
|
|
399 _fnDetectHeader( oSettings.aoFooter, oSettings.nTFoot );
|
|
400 }
|
|
401
|
|
402 /* Check if there is data passing into the constructor */
|
|
403 if ( bUsePassedData )
|
|
404 {
|
|
405 for ( i=0 ; i<oInit.aaData.length ; i++ )
|
|
406 {
|
|
407 _fnAddData( oSettings, oInit.aaData[ i ] );
|
|
408 }
|
|
409 }
|
|
410 else
|
|
411 {
|
|
412 /* Grab the data from the page */
|
|
413 _fnGatherData( oSettings );
|
|
414 }
|
|
415
|
|
416 /* Copy the data index array */
|
|
417 oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
|
|
418
|
|
419 /* Initialisation complete - table can be drawn */
|
|
420 oSettings.bInitialised = true;
|
|
421
|
|
422 /* Check if we need to initialise the table (it might not have been handed off to the
|
|
423 * language processor)
|
|
424 */
|
|
425 if ( bInitHandedOff === false )
|
|
426 {
|
|
427 _fnInitialise( oSettings );
|
|
428 }
|