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 |
} |