Mercurial > hg > ChinaGisRestApi
comparison gis_gui/lib/jquery.dataTables.js @ 63:7f008e782563
add gui files to product via FileSystemSite
author | casties |
---|---|
date | Fri, 05 Nov 2010 18:52:55 +0100 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
62:3905385c8854 | 63:7f008e782563 |
---|---|
1 /* | |
2 * File: jquery.dataTables.js | |
3 * Version: 1.6.2 | |
4 * CVS: $Id$ | |
5 * Description: Paginate, search and sort HTML tables | |
6 * Author: Allan Jardine (www.sprymedia.co.uk) | |
7 * Created: 28/3/2008 | |
8 * Modified: $Date$ by $Author$ | |
9 * Language: Javascript | |
10 * License: GPL v2 or BSD 3 point style | |
11 * Project: Mtaala | |
12 * Contact: allan.jardine@sprymedia.co.uk | |
13 * | |
14 * Copyright 2008-2010 Allan Jardine, all rights reserved. | |
15 * | |
16 * This source file is free software, under either the GPL v2 license or a | |
17 * BSD style license, as supplied with this software. | |
18 * | |
19 * This source file is distributed in the hope that it will be useful, but | |
20 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | |
21 * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details. | |
22 * | |
23 * For details pleease refer to: http://www.datatables.net | |
24 */ | |
25 | |
26 /* | |
27 * When considering jsLint, we need to allow eval() as it it is used for reading cookies and | |
28 * building the dynamic multi-column sort functions. | |
29 */ | |
30 /*jslint evil: true, undef: true, browser: true */ | |
31 /*globals $, jQuery,_fnReadCookie,_fnProcessingDisplay,_fnDraw,_fnSort,_fnReDraw,_fnDetectType,_fnSortingClasses,_fnSettingsFromNode,_fnBuildSearchArray,_fnCalculateEnd,_fnFeatureHtmlProcessing,_fnFeatureHtmlPaginate,_fnFeatureHtmlInfo,_fnUpdateInfo,_fnFeatureHtmlFilter,_fnFilter,_fnSaveState,_fnFilterColumn,_fnEscapeRegex,_fnFilterComplete,_fnFeatureHtmlLength,_fnGetDataMaster,_fnVisibleToColumnIndex,_fnDrawHead,_fnAddData,_fnGetTrNodes,_fnGetTdNodes,_fnColumnIndexToVisible,_fnCreateCookie,_fnAddOptionsHtml,_fnMap,_fnClearTable,_fnDataToSearch,_fnReOrderIndex,_fnFilterCustom,_fnNodeToDataIndex,_fnVisbleColumns,_fnAjaxUpdate,_fnAjaxUpdateDraw,_fnColumnOrdering,fnGetMaxLenString,_fnSortAttachListener,_fnPageChange*/ | |
32 | |
33 (function($) { | |
34 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * | |
35 * Section - DataTables variables | |
36 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ | |
37 | |
38 /* | |
39 * Variable: dataTableSettings | |
40 * Purpose: Store the settings for each dataTables instance | |
41 * Scope: jQuery.fn | |
42 */ | |
43 $.fn.dataTableSettings = []; | |
44 var _aoSettings = $.fn.dataTableSettings; /* Short reference for fast internal lookup */ | |
45 | |
46 /* | |
47 * Variable: dataTableExt | |
48 * Purpose: Container for customisable parts of DataTables | |
49 * Scope: jQuery.fn | |
50 */ | |
51 $.fn.dataTableExt = {}; | |
52 var _oExt = $.fn.dataTableExt; | |
53 | |
54 | |
55 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * | |
56 * Section - DataTables extensible objects | |
57 * | |
58 * The _oExt object is used to provide an area where user dfined plugins can be | |
59 * added to DataTables. The following properties of the object are used: | |
60 * oApi - Plug-in API functions | |
61 * aTypes - Auto-detection of types | |
62 * oSort - Sorting functions used by DataTables (based on the type) | |
63 * oPagination - Pagination functions for different input styles | |
64 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ | |
65 | |
66 /* | |
67 * Variable: sVersion | |
68 * Purpose: Version string for plug-ins to check compatibility | |
69 * Scope: jQuery.fn.dataTableExt | |
70 * Notes: Allowed format is a.b.c.d.e where: | |
71 * a:int, b:int, c:int, d:string(dev|beta), e:int. d and e are optional | |
72 */ | |
73 _oExt.sVersion = "1.6.2"; | |
74 | |
75 /* | |
76 * Variable: iApiIndex | |
77 * Purpose: Index for what 'this' index API functions should use | |
78 * Scope: jQuery.fn.dataTableExt | |
79 */ | |
80 _oExt.iApiIndex = 0; | |
81 | |
82 /* | |
83 * Variable: oApi | |
84 * Purpose: Container for plugin API functions | |
85 * Scope: jQuery.fn.dataTableExt | |
86 */ | |
87 _oExt.oApi = { }; | |
88 | |
89 /* | |
90 * Variable: aFiltering | |
91 * Purpose: Container for plugin filtering functions | |
92 * Scope: jQuery.fn.dataTableExt | |
93 */ | |
94 _oExt.afnFiltering = [ ]; | |
95 | |
96 /* | |
97 * Variable: aoFeatures | |
98 * Purpose: Container for plugin function functions | |
99 * Scope: jQuery.fn.dataTableExt | |
100 * Notes: Array of objects with the following parameters: | |
101 * fnInit: Function for initialisation of Feature. Takes oSettings and returns node | |
102 * cFeature: Character that will be matched in sDom - case sensitive | |
103 * sFeature: Feature name - just for completeness :-) | |
104 */ | |
105 _oExt.aoFeatures = [ ]; | |
106 | |
107 /* | |
108 * Variable: ofnSearch | |
109 * Purpose: Container for custom filtering functions | |
110 * Scope: jQuery.fn.dataTableExt | |
111 * Notes: This is an object (the name should match the type) for custom filtering function, | |
112 * which can be used for live DOM checking or formatted text filtering | |
113 */ | |
114 _oExt.ofnSearch = { }; | |
115 | |
116 /* | |
117 * Variable: afnSortData | |
118 * Purpose: Container for custom sorting data source functions | |
119 * Scope: jQuery.fn.dataTableExt | |
120 * Notes: Array (associative) of functions which is run prior to a column of this | |
121 * 'SortDataType' being sorted upon. | |
122 * Function input parameters: | |
123 * object:oSettings- DataTables settings object | |
124 * int:iColumn - Target column number | |
125 * Return value: Array of data which exactly matched the full data set size for the column to | |
126 * be sorted upon | |
127 */ | |
128 _oExt.afnSortData = [ ]; | |
129 | |
130 /* | |
131 * Variable: oStdClasses | |
132 * Purpose: Storage for the various classes that DataTables uses | |
133 * Scope: jQuery.fn.dataTableExt | |
134 */ | |
135 _oExt.oStdClasses = { | |
136 /* Two buttons buttons */ | |
137 "sPagePrevEnabled": "paginate_enabled_previous", | |
138 "sPagePrevDisabled": "paginate_disabled_previous", | |
139 "sPageNextEnabled": "paginate_enabled_next", | |
140 "sPageNextDisabled": "paginate_disabled_next", | |
141 "sPageJUINext": "", | |
142 "sPageJUIPrev": "", | |
143 | |
144 /* Full numbers paging buttons */ | |
145 "sPageButton": "paginate_button", | |
146 "sPageButtonActive": "paginate_active", | |
147 "sPageButtonStaticDisabled": "paginate_button", | |
148 "sPageFirst": "first", | |
149 "sPagePrevious": "previous", | |
150 "sPageNext": "next", | |
151 "sPageLast": "last", | |
152 | |
153 /* Stripping classes */ | |
154 "sStripOdd": "odd", | |
155 "sStripEven": "even", | |
156 | |
157 /* Empty row */ | |
158 "sRowEmpty": "dataTables_empty", | |
159 | |
160 /* Features */ | |
161 "sWrapper": "dataTables_wrapper", | |
162 "sFilter": "dataTables_filter", | |
163 "sInfo": "dataTables_info", | |
164 "sPaging": "dataTables_paginate paging_", /* Note that the type is postfixed */ | |
165 "sLength": "dataTables_length", | |
166 "sProcessing": "dataTables_processing", | |
167 | |
168 /* Sorting */ | |
169 "sSortAsc": "sorting_asc", | |
170 "sSortDesc": "sorting_desc", | |
171 "sSortable": "sorting", /* Sortable in both directions */ | |
172 "sSortableAsc": "sorting_asc_disabled", | |
173 "sSortableDesc": "sorting_desc_disabled", | |
174 "sSortableNone": "sorting_disabled", | |
175 "sSortColumn": "sorting_", /* Note that an int is postfixed for the sorting order */ | |
176 "sSortJUIAsc": "", | |
177 "sSortJUIDesc": "", | |
178 "sSortJUI": "", | |
179 "sSortJUIAscAllowed": "", | |
180 "sSortJUIDescAllowed": "" | |
181 }; | |
182 | |
183 /* | |
184 * Variable: oJUIClasses | |
185 * Purpose: Storage for the various classes that DataTables uses - jQuery UI suitable | |
186 * Scope: jQuery.fn.dataTableExt | |
187 */ | |
188 _oExt.oJUIClasses = { | |
189 /* Two buttons buttons */ | |
190 "sPagePrevEnabled": "fg-button ui-state-default ui-corner-left", | |
191 "sPagePrevDisabled": "fg-button ui-state-default ui-corner-left ui-state-disabled", | |
192 "sPageNextEnabled": "fg-button ui-state-default ui-corner-right", | |
193 "sPageNextDisabled": "fg-button ui-state-default ui-corner-right ui-state-disabled", | |
194 "sPageJUINext": "ui-icon ui-icon-circle-arrow-e", | |
195 "sPageJUIPrev": "ui-icon ui-icon-circle-arrow-w", | |
196 | |
197 /* Full numbers paging buttons */ | |
198 "sPageButton": "fg-button ui-state-default", | |
199 "sPageButtonActive": "fg-button ui-state-default ui-state-disabled", | |
200 "sPageButtonStaticDisabled": "fg-button ui-state-default ui-state-disabled", | |
201 "sPageFirst": "first ui-corner-tl ui-corner-bl", | |
202 "sPagePrevious": "previous", | |
203 "sPageNext": "next", | |
204 "sPageLast": "last ui-corner-tr ui-corner-br", | |
205 | |
206 /* Stripping classes */ | |
207 "sStripOdd": "odd", | |
208 "sStripEven": "even", | |
209 | |
210 /* Empty row */ | |
211 "sRowEmpty": "dataTables_empty", | |
212 | |
213 /* Features */ | |
214 "sWrapper": "dataTables_wrapper", | |
215 "sFilter": "dataTables_filter", | |
216 "sInfo": "dataTables_info", | |
217 "sPaging": "dataTables_paginate fg-buttonset fg-buttonset-multi paging_", /* Note that the type is postfixed */ | |
218 "sLength": "dataTables_length", | |
219 "sProcessing": "dataTables_processing", | |
220 | |
221 /* Sorting */ | |
222 "sSortAsc": "ui-state-default", | |
223 "sSortDesc": "ui-state-default", | |
224 "sSortable": "ui-state-default", | |
225 "sSortableAsc": "ui-state-default", | |
226 "sSortableDesc": "ui-state-default", | |
227 "sSortableNone": "ui-state-default", | |
228 "sSortColumn": "sorting_", /* Note that an int is postfixed for the sorting order */ | |
229 "sSortJUIAsc": "css_right ui-icon ui-icon-triangle-1-n", | |
230 "sSortJUIDesc": "css_right ui-icon ui-icon-triangle-1-s", | |
231 "sSortJUI": "css_right ui-icon ui-icon-carat-2-n-s", | |
232 "sSortJUIAscAllowed": "css_right ui-icon ui-icon-carat-1-n", | |
233 "sSortJUIDescAllowed": "css_right ui-icon ui-icon-carat-1-s" | |
234 }; | |
235 | |
236 /* | |
237 * Variable: oPagination | |
238 * Purpose: Container for the various type of pagination that dataTables supports | |
239 * Scope: jQuery.fn.dataTableExt | |
240 */ | |
241 _oExt.oPagination = { | |
242 /* | |
243 * Variable: two_button | |
244 * Purpose: Standard two button (forward/back) pagination | |
245 * Scope: jQuery.fn.dataTableExt.oPagination | |
246 */ | |
247 "two_button": { | |
248 /* | |
249 * Function: oPagination.two_button.fnInit | |
250 * Purpose: Initalise dom elements required for pagination with forward/back buttons only | |
251 * Returns: - | |
252 * Inputs: object:oSettings - dataTables settings object | |
253 * node:nPaging - the DIV which contains this pagination control | |
254 * function:fnCallbackDraw - draw function which must be called on update | |
255 */ | |
256 "fnInit": function ( oSettings, nPaging, fnCallbackDraw ) | |
257 { | |
258 var nPrevious, nNext, nPreviousInner, nNextInner; | |
259 | |
260 /* Store the next and previous elements in the oSettings object as they can be very | |
261 * usful for automation - particularly testing | |
262 */ | |
263 if ( !oSettings.bJUI ) | |
264 { | |
265 nPrevious = document.createElement( 'div' ); | |
266 nNext = document.createElement( 'div' ); | |
267 } | |
268 else | |
269 { | |
270 nPrevious = document.createElement( 'a' ); | |
271 nNext = document.createElement( 'a' ); | |
272 | |
273 nNextInner = document.createElement('span'); | |
274 nNextInner.className = oSettings.oClasses.sPageJUINext; | |
275 nNext.appendChild( nNextInner ); | |
276 | |
277 nPreviousInner = document.createElement('span'); | |
278 nPreviousInner.className = oSettings.oClasses.sPageJUIPrev; | |
279 nPrevious.appendChild( nPreviousInner ); | |
280 } | |
281 | |
282 nPrevious.className = oSettings.oClasses.sPagePrevDisabled; | |
283 nNext.className = oSettings.oClasses.sPageNextDisabled; | |
284 | |
285 nPrevious.title = oSettings.oLanguage.oPaginate.sPrevious; | |
286 nNext.title = oSettings.oLanguage.oPaginate.sNext; | |
287 | |
288 nPaging.appendChild( nPrevious ); | |
289 nPaging.appendChild( nNext ); | |
290 | |
291 $(nPrevious).click( function() { | |
292 if ( oSettings.oApi._fnPageChange( oSettings, "previous" ) ) | |
293 { | |
294 /* Only draw when the page has actually changed */ | |
295 fnCallbackDraw( oSettings ); | |
296 } | |
297 } ); | |
298 | |
299 $(nNext).click( function() { | |
300 if ( oSettings.oApi._fnPageChange( oSettings, "next" ) ) | |
301 { | |
302 fnCallbackDraw( oSettings ); | |
303 } | |
304 } ); | |
305 | |
306 /* Take the brutal approach to cancelling text selection */ | |
307 $(nPrevious).bind( 'selectstart', function () { return false; } ); | |
308 $(nNext).bind( 'selectstart', function () { return false; } ); | |
309 | |
310 /* ID the first elements only */ | |
311 if ( oSettings.sTableId !== '' && typeof oSettings.aanFeatures.p == "undefined" ) | |
312 { | |
313 nPaging.setAttribute( 'id', oSettings.sTableId+'_paginate' ); | |
314 nPrevious.setAttribute( 'id', oSettings.sTableId+'_previous' ); | |
315 nNext.setAttribute( 'id', oSettings.sTableId+'_next' ); | |
316 } | |
317 }, | |
318 | |
319 /* | |
320 * Function: oPagination.two_button.fnUpdate | |
321 * Purpose: Update the two button pagination at the end of the draw | |
322 * Returns: - | |
323 * Inputs: object:oSettings - dataTables settings object | |
324 * function:fnCallbackDraw - draw function to call on page change | |
325 */ | |
326 "fnUpdate": function ( oSettings, fnCallbackDraw ) | |
327 { | |
328 if ( !oSettings.aanFeatures.p ) | |
329 { | |
330 return; | |
331 } | |
332 | |
333 /* Loop over each instance of the pager */ | |
334 var an = oSettings.aanFeatures.p; | |
335 for ( var i=0, iLen=an.length ; i<iLen ; i++ ) | |
336 { | |
337 if ( an[i].childNodes.length !== 0 ) | |
338 { | |
339 an[i].childNodes[0].className = | |
340 ( oSettings._iDisplayStart === 0 ) ? | |
341 oSettings.oClasses.sPagePrevDisabled : oSettings.oClasses.sPagePrevEnabled; | |
342 | |
343 an[i].childNodes[1].className = | |
344 ( oSettings.fnDisplayEnd() == oSettings.fnRecordsDisplay() ) ? | |
345 oSettings.oClasses.sPageNextDisabled : oSettings.oClasses.sPageNextEnabled; | |
346 } | |
347 } | |
348 } | |
349 }, | |
350 | |
351 | |
352 /* | |
353 * Variable: iFullNumbersShowPages | |
354 * Purpose: Change the number of pages which can be seen | |
355 * Scope: jQuery.fn.dataTableExt.oPagination | |
356 */ | |
357 "iFullNumbersShowPages": 5, | |
358 | |
359 /* | |
360 * Variable: full_numbers | |
361 * Purpose: Full numbers pagination | |
362 * Scope: jQuery.fn.dataTableExt.oPagination | |
363 */ | |
364 "full_numbers": { | |
365 /* | |
366 * Function: oPagination.full_numbers.fnInit | |
367 * Purpose: Initalise dom elements required for pagination with a list of the pages | |
368 * Returns: - | |
369 * Inputs: object:oSettings - dataTables settings object | |
370 * node:nPaging - the DIV which contains this pagination control | |
371 * function:fnCallbackDraw - draw function which must be called on update | |
372 */ | |
373 "fnInit": function ( oSettings, nPaging, fnCallbackDraw ) | |
374 { | |
375 var nFirst = document.createElement( 'span' ); | |
376 var nPrevious = document.createElement( 'span' ); | |
377 var nList = document.createElement( 'span' ); | |
378 var nNext = document.createElement( 'span' ); | |
379 var nLast = document.createElement( 'span' ); | |
380 | |
381 nFirst.innerHTML = oSettings.oLanguage.oPaginate.sFirst; | |
382 nPrevious.innerHTML = oSettings.oLanguage.oPaginate.sPrevious; | |
383 nNext.innerHTML = oSettings.oLanguage.oPaginate.sNext; | |
384 nLast.innerHTML = oSettings.oLanguage.oPaginate.sLast; | |
385 | |
386 var oClasses = oSettings.oClasses; | |
387 nFirst.className = oClasses.sPageButton+" "+oClasses.sPageFirst; | |
388 nPrevious.className = oClasses.sPageButton+" "+oClasses.sPagePrevious; | |
389 nNext.className= oClasses.sPageButton+" "+oClasses.sPageNext; | |
390 nLast.className = oClasses.sPageButton+" "+oClasses.sPageLast; | |
391 | |
392 nPaging.appendChild( nFirst ); | |
393 nPaging.appendChild( nPrevious ); | |
394 nPaging.appendChild( nList ); | |
395 nPaging.appendChild( nNext ); | |
396 nPaging.appendChild( nLast ); | |
397 | |
398 $(nFirst).click( function () { | |
399 if ( oSettings.oApi._fnPageChange( oSettings, "first" ) ) | |
400 { | |
401 fnCallbackDraw( oSettings ); | |
402 } | |
403 } ); | |
404 | |
405 $(nPrevious).click( function() { | |
406 if ( oSettings.oApi._fnPageChange( oSettings, "previous" ) ) | |
407 { | |
408 fnCallbackDraw( oSettings ); | |
409 } | |
410 } ); | |
411 | |
412 $(nNext).click( function() { | |
413 if ( oSettings.oApi._fnPageChange( oSettings, "next" ) ) | |
414 { | |
415 fnCallbackDraw( oSettings ); | |
416 } | |
417 } ); | |
418 | |
419 $(nLast).click( function() { | |
420 if ( oSettings.oApi._fnPageChange( oSettings, "last" ) ) | |
421 { | |
422 fnCallbackDraw( oSettings ); | |
423 } | |
424 } ); | |
425 | |
426 /* Take the brutal approach to cancelling text selection */ | |
427 $('span', nPaging) | |
428 .bind( 'mousedown', function () { return false; } ) | |
429 .bind( 'selectstart', function () { return false; } ); | |
430 | |
431 /* ID the first elements only */ | |
432 if ( oSettings.sTableId !== '' && typeof oSettings.aanFeatures.p == "undefined" ) | |
433 { | |
434 nPaging.setAttribute( 'id', oSettings.sTableId+'_paginate' ); | |
435 nFirst.setAttribute( 'id', oSettings.sTableId+'_first' ); | |
436 nPrevious.setAttribute( 'id', oSettings.sTableId+'_previous' ); | |
437 nNext.setAttribute( 'id', oSettings.sTableId+'_next' ); | |
438 nLast.setAttribute( 'id', oSettings.sTableId+'_last' ); | |
439 } | |
440 }, | |
441 | |
442 /* | |
443 * Function: oPagination.full_numbers.fnUpdate | |
444 * Purpose: Update the list of page buttons shows | |
445 * Returns: - | |
446 * Inputs: object:oSettings - dataTables settings object | |
447 * function:fnCallbackDraw - draw function to call on page change | |
448 */ | |
449 "fnUpdate": function ( oSettings, fnCallbackDraw ) | |
450 { | |
451 if ( !oSettings.aanFeatures.p ) | |
452 { | |
453 return; | |
454 } | |
455 | |
456 var iPageCount = _oExt.oPagination.iFullNumbersShowPages; | |
457 var iPageCountHalf = Math.floor(iPageCount / 2); | |
458 var iPages = Math.ceil((oSettings.fnRecordsDisplay()) / oSettings._iDisplayLength); | |
459 var iCurrentPage = Math.ceil(oSettings._iDisplayStart / oSettings._iDisplayLength) + 1; | |
460 var sList = ""; | |
461 var iStartButton, iEndButton, i, iLen; | |
462 var oClasses = oSettings.oClasses; | |
463 | |
464 /* Pages calculation */ | |
465 if (iPages < iPageCount) | |
466 { | |
467 iStartButton = 1; | |
468 iEndButton = iPages; | |
469 } | |
470 else | |
471 { | |
472 if (iCurrentPage <= iPageCountHalf) | |
473 { | |
474 iStartButton = 1; | |
475 iEndButton = iPageCount; | |
476 } | |
477 else | |
478 { | |
479 if (iCurrentPage >= (iPages - iPageCountHalf)) | |
480 { | |
481 iStartButton = iPages - iPageCount + 1; | |
482 iEndButton = iPages; | |
483 } | |
484 else | |
485 { | |
486 iStartButton = iCurrentPage - Math.ceil(iPageCount / 2) + 1; | |
487 iEndButton = iStartButton + iPageCount - 1; | |
488 } | |
489 } | |
490 } | |
491 | |
492 /* Build the dynamic list */ | |
493 for ( i=iStartButton ; i<=iEndButton ; i++ ) | |
494 { | |
495 if ( iCurrentPage != i ) | |
496 { | |
497 sList += '<span class="'+oClasses.sPageButton+'">'+i+'</span>'; | |
498 } | |
499 else | |
500 { | |
501 sList += '<span class="'+oClasses.sPageButtonActive+'">'+i+'</span>'; | |
502 } | |
503 } | |
504 | |
505 /* Loop over each instance of the pager */ | |
506 var an = oSettings.aanFeatures.p; | |
507 var anButtons, anStatic, nPaginateList; | |
508 var fnClick = function() { | |
509 /* Use the information in the element to jump to the required page */ | |
510 var iTarget = (this.innerHTML * 1) - 1; | |
511 oSettings._iDisplayStart = iTarget * oSettings._iDisplayLength; | |
512 fnCallbackDraw( oSettings ); | |
513 return false; | |
514 }; | |
515 var fnFalse = function () { return false; }; | |
516 | |
517 for ( i=0, iLen=an.length ; i<iLen ; i++ ) | |
518 { | |
519 if ( an[i].childNodes.length === 0 ) | |
520 { | |
521 continue; | |
522 } | |
523 | |
524 /* Build up the dynamic list forst - html and listeners */ | |
525 nPaginateList = an[i].childNodes[2]; | |
526 nPaginateList.innerHTML = sList; | |
527 | |
528 $('span', nPaginateList).click( fnClick ).bind( 'mousedown', fnFalse ) | |
529 .bind( 'selectstart', fnFalse ); | |
530 | |
531 /* Update the 'premanent botton's classes */ | |
532 anButtons = an[i].getElementsByTagName('span'); | |
533 anStatic = [ | |
534 anButtons[0], anButtons[1], | |
535 anButtons[anButtons.length-2], anButtons[anButtons.length-1] | |
536 ]; | |
537 $(anStatic).removeClass( oClasses.sPageButton+" "+oClasses.sPageButtonActive+" "+oClasses.sPageButtonStaticDisabled ); | |
538 if ( iCurrentPage == 1 ) | |
539 { | |
540 anStatic[0].className += " "+oClasses.sPageButtonStaticDisabled; | |
541 anStatic[1].className += " "+oClasses.sPageButtonStaticDisabled; | |
542 } | |
543 else | |
544 { | |
545 anStatic[0].className += " "+oClasses.sPageButton; | |
546 anStatic[1].className += " "+oClasses.sPageButton; | |
547 } | |
548 | |
549 if ( iPages === 0 || iCurrentPage == iPages || oSettings._iDisplayLength == -1 ) | |
550 { | |
551 anStatic[2].className += " "+oClasses.sPageButtonStaticDisabled; | |
552 anStatic[3].className += " "+oClasses.sPageButtonStaticDisabled; | |
553 } | |
554 else | |
555 { | |
556 anStatic[2].className += " "+oClasses.sPageButton; | |
557 anStatic[3].className += " "+oClasses.sPageButton; | |
558 } | |
559 } | |
560 } | |
561 } | |
562 }; | |
563 | |
564 /* | |
565 * Variable: oSort | |
566 * Purpose: Wrapper for the sorting functions that can be used in DataTables | |
567 * Scope: jQuery.fn.dataTableExt | |
568 * Notes: The functions provided in this object are basically standard javascript sort | |
569 * functions - they expect two inputs which they then compare and then return a priority | |
570 * result. For each sort method added, two functions need to be defined, an ascending sort and | |
571 * a descending sort. | |
572 */ | |
573 _oExt.oSort = { | |
574 /* | |
575 * text sorting | |
576 */ | |
577 "string-asc": function ( a, b ) | |
578 { | |
579 var x = a.toLowerCase(); | |
580 var y = b.toLowerCase(); | |
581 return ((x < y) ? -1 : ((x > y) ? 1 : 0)); | |
582 }, | |
583 | |
584 "string-desc": function ( a, b ) | |
585 { | |
586 var x = a.toLowerCase(); | |
587 var y = b.toLowerCase(); | |
588 return ((x < y) ? 1 : ((x > y) ? -1 : 0)); | |
589 }, | |
590 | |
591 | |
592 /* | |
593 * html sorting (ignore html tags) | |
594 */ | |
595 "html-asc": function ( a, b ) | |
596 { | |
597 var x = a.replace( /<.*?>/g, "" ).toLowerCase(); | |
598 var y = b.replace( /<.*?>/g, "" ).toLowerCase(); | |
599 return ((x < y) ? -1 : ((x > y) ? 1 : 0)); | |
600 }, | |
601 | |
602 "html-desc": function ( a, b ) | |
603 { | |
604 var x = a.replace( /<.*?>/g, "" ).toLowerCase(); | |
605 var y = b.replace( /<.*?>/g, "" ).toLowerCase(); | |
606 return ((x < y) ? 1 : ((x > y) ? -1 : 0)); | |
607 }, | |
608 | |
609 | |
610 /* | |
611 * date sorting | |
612 */ | |
613 "date-asc": function ( a, b ) | |
614 { | |
615 var x = Date.parse( a ); | |
616 var y = Date.parse( b ); | |
617 | |
618 if ( isNaN( x ) ) | |
619 { | |
620 x = Date.parse( "01/01/1970 00:00:00" ); | |
621 } | |
622 if ( isNaN( y ) ) | |
623 { | |
624 y = Date.parse( "01/01/1970 00:00:00" ); | |
625 } | |
626 | |
627 return x - y; | |
628 }, | |
629 | |
630 "date-desc": function ( a, b ) | |
631 { | |
632 var x = Date.parse( a ); | |
633 var y = Date.parse( b ); | |
634 | |
635 if ( isNaN( x ) ) | |
636 { | |
637 x = Date.parse( "01/01/1970 00:00:00" ); | |
638 } | |
639 if ( isNaN( y ) ) | |
640 { | |
641 y = Date.parse( "01/01/1970 00:00:00" ); | |
642 } | |
643 | |
644 return y - x; | |
645 }, | |
646 | |
647 | |
648 /* | |
649 * numerical sorting | |
650 */ | |
651 "numeric-asc": function ( a, b ) | |
652 { | |
653 var x = a == "-" ? 0 : a; | |
654 var y = b == "-" ? 0 : b; | |
655 return x - y; | |
656 }, | |
657 | |
658 "numeric-desc": function ( a, b ) | |
659 { | |
660 var x = a == "-" ? 0 : a; | |
661 var y = b == "-" ? 0 : b; | |
662 return y - x; | |
663 } | |
664 }; | |
665 | |
666 | |
667 /* | |
668 * Variable: aTypes | |
669 * Purpose: Container for the various type of type detection that dataTables supports | |
670 * Scope: jQuery.fn.dataTableExt | |
671 * Notes: The functions in this array are expected to parse a string to see if it is a data | |
672 * type that it recognises. If so then the function should return the name of the type (a | |
673 * corresponding sort function should be defined!), if the type is not recognised then the | |
674 * function should return null such that the parser and move on to check the next type. | |
675 * Note that ordering is important in this array - the functions are processed linearly, | |
676 * starting at index 0. | |
677 */ | |
678 _oExt.aTypes = [ | |
679 /* | |
680 * Function: - | |
681 * Purpose: Check to see if a string is numeric | |
682 * Returns: string:'numeric' or null | |
683 * Inputs: string:sText - string to check | |
684 */ | |
685 function ( sData ) | |
686 { | |
687 /* Sanity check that we are dealing with a string or quick return for a number */ | |
688 if ( typeof sData == 'number' ) | |
689 { | |
690 return 'numeric'; | |
691 } | |
692 else if ( typeof sData.charAt != 'function' ) | |
693 { | |
694 return null; | |
695 } | |
696 | |
697 var sValidFirstChars = "0123456789-"; | |
698 var sValidChars = "0123456789."; | |
699 var Char; | |
700 var bDecimal = false; | |
701 | |
702 /* Check for a valid first char (no period and allow negatives) */ | |
703 Char = sData.charAt(0); | |
704 if (sValidFirstChars.indexOf(Char) == -1) | |
705 { | |
706 return null; | |
707 } | |
708 | |
709 /* Check all the other characters are valid */ | |
710 for ( var i=1 ; i<sData.length ; i++ ) | |
711 { | |
712 Char = sData.charAt(i); | |
713 if (sValidChars.indexOf(Char) == -1) | |
714 { | |
715 return null; | |
716 } | |
717 | |
718 /* Only allowed one decimal place... */ | |
719 if ( Char == "." ) | |
720 { | |
721 if ( bDecimal ) | |
722 { | |
723 return null; | |
724 } | |
725 bDecimal = true; | |
726 } | |
727 } | |
728 | |
729 return 'numeric'; | |
730 }, | |
731 | |
732 /* | |
733 * Function: - | |
734 * Purpose: Check to see if a string is actually a formatted date | |
735 * Returns: string:'date' or null | |
736 * Inputs: string:sText - string to check | |
737 */ | |
738 function ( sData ) | |
739 { | |
740 var iParse = Date.parse(sData); | |
741 if ( iParse !== null && !isNaN(iParse) ) | |
742 { | |
743 return 'date'; | |
744 } | |
745 return null; | |
746 } | |
747 ]; | |
748 | |
749 | |
750 /* | |
751 * Variable: _oExternConfig | |
752 * Purpose: Store information for DataTables to access globally about other instances | |
753 * Scope: jQuery.fn.dataTableExt | |
754 */ | |
755 _oExt._oExternConfig = { | |
756 /* int:iNextUnique - next unique number for an instance */ | |
757 "iNextUnique": 0 | |
758 }; | |
759 | |
760 | |
761 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * | |
762 * Section - DataTables prototype | |
763 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ | |
764 | |
765 /* | |
766 * Function: dataTable | |
767 * Purpose: DataTables information | |
768 * Returns: - | |
769 * Inputs: object:oInit - initalisation options for the table | |
770 */ | |
771 $.fn.dataTable = function( oInit ) | |
772 { | |
773 /* | |
774 * Function: classSettings | |
775 * Purpose: Settings container function for all 'class' properties which are required | |
776 * by dataTables | |
777 * Returns: - | |
778 * Inputs: - | |
779 */ | |
780 function classSettings () | |
781 { | |
782 this.fnRecordsTotal = function () | |
783 { | |
784 if ( this.oFeatures.bServerSide ) { | |
785 return this._iRecordsTotal; | |
786 } else { | |
787 return this.aiDisplayMaster.length; | |
788 } | |
789 }; | |
790 | |
791 this.fnRecordsDisplay = function () | |
792 { | |
793 if ( this.oFeatures.bServerSide ) { | |
794 return this._iRecordsDisplay; | |
795 } else { | |
796 return this.aiDisplay.length; | |
797 } | |
798 }; | |
799 | |
800 this.fnDisplayEnd = function () | |
801 { | |
802 if ( this.oFeatures.bServerSide ) { | |
803 return this._iDisplayStart + this.aiDisplay.length; | |
804 } else { | |
805 return this._iDisplayEnd; | |
806 } | |
807 }; | |
808 | |
809 /* | |
810 * Variable: sInstance | |
811 * Purpose: Unique idendifier for each instance of the DataTables object | |
812 * Scope: jQuery.dataTable.classSettings | |
813 */ | |
814 this.sInstance = null; | |
815 | |
816 /* | |
817 * Variable: oFeatures | |
818 * Purpose: Indicate the enablement of key dataTable features | |
819 * Scope: jQuery.dataTable.classSettings | |
820 */ | |
821 this.oFeatures = { | |
822 "bPaginate": true, | |
823 "bLengthChange": true, | |
824 "bFilter": true, | |
825 "bSort": true, | |
826 "bInfo": true, | |
827 "bAutoWidth": true, | |
828 "bProcessing": false, | |
829 "bSortClasses": true, | |
830 "bStateSave": false, | |
831 "bServerSide": false | |
832 }; | |
833 | |
834 /* | |
835 * Variable: aanFeatures | |
836 * Purpose: Array referencing the nodes which are used for the features | |
837 * Scope: jQuery.dataTable.classSettings | |
838 * Notes: The parameters of this object match what is allowed by sDom - i.e. | |
839 * 'l' - Length changing | |
840 * 'f' - Filtering input | |
841 * 't' - The table! | |
842 * 'i' - Information | |
843 * 'p' - Pagination | |
844 * 'r' - pRocessing | |
845 */ | |
846 this.aanFeatures = []; | |
847 | |
848 /* | |
849 * Variable: oLanguage | |
850 * Purpose: Store the language strings used by dataTables | |
851 * Scope: jQuery.dataTable.classSettings | |
852 * Notes: The words in the format _VAR_ are variables which are dynamically replaced | |
853 * by javascript | |
854 */ | |
855 this.oLanguage = { | |
856 "sProcessing": "Processing...", | |
857 "sLengthMenu": "Show _MENU_ entries", | |
858 "sZeroRecords": "No matching records found", | |
859 "sInfo": "Showing _START_ to _END_ of _TOTAL_ entries", | |
860 "sInfoEmpty": "Showing 0 to 0 of 0 entries", | |
861 "sInfoFiltered": "(filtered from _MAX_ total entries)", | |
862 "sInfoPostFix": "", | |
863 "sSearch": "Search:", | |
864 "sUrl": "", | |
865 "oPaginate": { | |
866 "sFirst": "First", | |
867 "sPrevious": "Previous", | |
868 "sNext": "Next", | |
869 "sLast": "Last" | |
870 } | |
871 }; | |
872 | |
873 /* | |
874 * Variable: aoData | |
875 * Purpose: Store data information | |
876 * Scope: jQuery.dataTable.classSettings | |
877 * Notes: This is an array of objects with the following parameters: | |
878 * int: _iId - internal id for tracking | |
879 * array: _aData - internal data - used for sorting / filtering etc | |
880 * node: nTr - display node | |
881 * array node: _anHidden - hidden TD nodes | |
882 * string: _sRowStripe | |
883 */ | |
884 this.aoData = []; | |
885 | |
886 /* | |
887 * Variable: aiDisplay | |
888 * Purpose: Array of indexes which are in the current display (after filtering etc) | |
889 * Scope: jQuery.dataTable.classSettings | |
890 */ | |
891 this.aiDisplay = []; | |
892 | |
893 /* | |
894 * Variable: aiDisplayMaster | |
895 * Purpose: Array of indexes for display - no filtering | |
896 * Scope: jQuery.dataTable.classSettings | |
897 */ | |
898 this.aiDisplayMaster = []; | |
899 | |
900 /* | |
901 * Variable: aoColumns | |
902 * Purpose: Store information about each column that is in use | |
903 * Scope: jQuery.dataTable.classSettings | |
904 */ | |
905 this.aoColumns = []; | |
906 | |
907 /* | |
908 * Variable: iNextId | |
909 * Purpose: Store the next unique id to be used for a new row | |
910 * Scope: jQuery.dataTable.classSettings | |
911 */ | |
912 this.iNextId = 0; | |
913 | |
914 /* | |
915 * Variable: asDataSearch | |
916 * Purpose: Search data array for regular expression searching | |
917 * Scope: jQuery.dataTable.classSettings | |
918 */ | |
919 this.asDataSearch = []; | |
920 | |
921 /* | |
922 * Variable: oPreviousSearch | |
923 * Purpose: Store the previous search incase we want to force a re-search | |
924 * or compare the old search to a new one | |
925 * Scope: jQuery.dataTable.classSettings | |
926 */ | |
927 this.oPreviousSearch = { | |
928 "sSearch": "", | |
929 "bEscapeRegex": true | |
930 }; | |
931 | |
932 /* | |
933 * Variable: aoPreSearchCols | |
934 * Purpose: Store the previous search for each column | |
935 * Scope: jQuery.dataTable.classSettings | |
936 */ | |
937 this.aoPreSearchCols = []; | |
938 | |
939 /* | |
940 * Variable: aaSorting | |
941 * Purpose: Sorting information | |
942 * Scope: jQuery.dataTable.classSettings | |
943 * Notes: Index 0 - column number | |
944 * Index 1 - current sorting direction | |
945 * Index 2 - index of asSorting for this column | |
946 */ | |
947 this.aaSorting = [ [0, 'asc', 0] ]; | |
948 | |
949 /* | |
950 * Variable: aaSortingFixed | |
951 * Purpose: Sorting information that is always applied | |
952 * Scope: jQuery.dataTable.classSettings | |
953 */ | |
954 this.aaSortingFixed = null; | |
955 | |
956 /* | |
957 * Variable: asStripClasses | |
958 * Purpose: Classes to use for the striping of a table | |
959 * Scope: jQuery.dataTable.classSettings | |
960 */ | |
961 this.asStripClasses = []; | |
962 | |
963 /* | |
964 * Variable: fnRowCallback | |
965 * Purpose: Call this function every time a row is inserted (draw) | |
966 * Scope: jQuery.dataTable.classSettings | |
967 */ | |
968 this.fnRowCallback = null; | |
969 | |
970 /* | |
971 * Variable: fnHeaderCallback | |
972 * Purpose: Callback function for the header on each draw | |
973 * Scope: jQuery.dataTable.classSettings | |
974 */ | |
975 this.fnHeaderCallback = null; | |
976 | |
977 /* | |
978 * Variable: fnFooterCallback | |
979 * Purpose: Callback function for the footer on each draw | |
980 * Scope: jQuery.dataTable.classSettings | |
981 */ | |
982 this.fnFooterCallback = null; | |
983 | |
984 /* | |
985 * Variable: aoDrawCallback | |
986 * Purpose: Array of callback functions for draw callback functions | |
987 * Scope: jQuery.dataTable.classSettings | |
988 * Notes: Each array element is an object with the following parameters: | |
989 * function:fn - function to call | |
990 * string:sName - name callback (feature). useful for arranging array | |
991 */ | |
992 this.aoDrawCallback = []; | |
993 | |
994 /* | |
995 * Variable: fnInitComplete | |
996 * Purpose: Callback function for when the table has been initalised | |
997 * Scope: jQuery.dataTable.classSettings | |
998 */ | |
999 this.fnInitComplete = null; | |
1000 | |
1001 /* | |
1002 * Variable: sTableId | |
1003 * Purpose: Cache the table ID for quick access | |
1004 * Scope: jQuery.dataTable.classSettings | |
1005 */ | |
1006 this.sTableId = ""; | |
1007 | |
1008 /* | |
1009 * Variable: nTable | |
1010 * Purpose: Cache the table node for quick access | |
1011 * Scope: jQuery.dataTable.classSettings | |
1012 */ | |
1013 this.nTable = null; | |
1014 | |
1015 /* | |
1016 * Variable: iDefaultSortIndex | |
1017 * Purpose: Sorting index which will be used by default | |
1018 * Scope: jQuery.dataTable.classSettings | |
1019 */ | |
1020 this.iDefaultSortIndex = 0; | |
1021 | |
1022 /* | |
1023 * Variable: bInitialised | |
1024 * Purpose: Indicate if all required information has been read in | |
1025 * Scope: jQuery.dataTable.classSettings | |
1026 */ | |
1027 this.bInitialised = false; | |
1028 | |
1029 /* | |
1030 * Variable: aoOpenRows | |
1031 * Purpose: Information about open rows | |
1032 * Scope: jQuery.dataTable.classSettings | |
1033 * Notes: Has the parameters 'nTr' and 'nParent' | |
1034 */ | |
1035 this.aoOpenRows = []; | |
1036 | |
1037 /* | |
1038 * Variable: sDom | |
1039 * Purpose: Dictate the positioning that the created elements will take | |
1040 * Scope: jQuery.dataTable.classSettings | |
1041 * Notes: | |
1042 * The following options are allowed: | |
1043 * 'l' - Length changing | |
1044 * 'f' - Filtering input | |
1045 * 't' - The table! | |
1046 * 'i' - Information | |
1047 * 'p' - Pagination | |
1048 * 'r' - pRocessing | |
1049 * The following constants are allowed: | |
1050 * 'H' - jQueryUI theme "header" classes | |
1051 * 'F' - jQueryUI theme "footer" classes | |
1052 * The following syntax is expected: | |
1053 * '<' and '>' - div elements | |
1054 * '<"class" and '>' - div with a class | |
1055 * Examples: | |
1056 * '<"wrapper"flipt>', '<lf<t>ip>' | |
1057 */ | |
1058 this.sDom = 'lfrtip'; | |
1059 | |
1060 /* | |
1061 * Variable: sPaginationType | |
1062 * Purpose: Note which type of sorting should be used | |
1063 * Scope: jQuery.dataTable.classSettings | |
1064 */ | |
1065 this.sPaginationType = "two_button"; | |
1066 | |
1067 /* | |
1068 * Variable: iCookieDuration | |
1069 * Purpose: The cookie duration (for bStateSave) in seconds - default 2 hours | |
1070 * Scope: jQuery.dataTable.classSettings | |
1071 */ | |
1072 this.iCookieDuration = 60 * 60 * 2; | |
1073 | |
1074 /* | |
1075 * Variable: sAjaxSource | |
1076 * Purpose: Source url for AJAX data for the table | |
1077 * Scope: jQuery.dataTable.classSettings | |
1078 */ | |
1079 this.sAjaxSource = null; | |
1080 | |
1081 /* | |
1082 * Variable: bAjaxDataGet | |
1083 * Purpose: Note if draw should be blocked while getting data | |
1084 * Scope: jQuery.dataTable.classSettings | |
1085 */ | |
1086 this.bAjaxDataGet = true; | |
1087 | |
1088 /* | |
1089 * Variable: fnServerData | |
1090 * Purpose: Function to get the server-side data - can be overruled by the developer | |
1091 * Scope: jQuery.dataTable.classSettings | |
1092 */ | |
1093 this.fnServerData = $.getJSON; | |
1094 | |
1095 /* | |
1096 * Variable: iServerDraw | |
1097 * Purpose: Counter and tracker for server-side processing draws | |
1098 * Scope: jQuery.dataTable.classSettings | |
1099 */ | |
1100 this.iServerDraw = 0; | |
1101 | |
1102 /* | |
1103 * Variable: _iDisplayLength, _iDisplayStart, _iDisplayEnd | |
1104 * Purpose: Display length variables | |
1105 * Scope: jQuery.dataTable.classSettings | |
1106 * Notes: These variable must NOT be used externally to get the data length. Rather, use | |
1107 * the fnRecordsTotal() (etc) functions. | |
1108 */ | |
1109 this._iDisplayLength = 10; | |
1110 this._iDisplayStart = 0; | |
1111 this._iDisplayEnd = 10; | |
1112 | |
1113 /* | |
1114 * Variable: _iRecordsTotal, _iRecordsDisplay | |
1115 * Purpose: Display length variables used for server side processing | |
1116 * Scope: jQuery.dataTable.classSettings | |
1117 * Notes: These variable must NOT be used externally to get the data length. Rather, use | |
1118 * the fnRecordsTotal() (etc) functions. | |
1119 */ | |
1120 this._iRecordsTotal = 0; | |
1121 this._iRecordsDisplay = 0; | |
1122 | |
1123 /* | |
1124 * Variable: bJUI | |
1125 * Purpose: Should we add the markup needed for jQuery UI theming? | |
1126 * Scope: jQuery.dataTable.classSettings | |
1127 */ | |
1128 this.bJUI = false; | |
1129 | |
1130 /* | |
1131 * Variable: bJUI | |
1132 * Purpose: Should we add the markup needed for jQuery UI theming? | |
1133 * Scope: jQuery.dataTable.classSettings | |
1134 */ | |
1135 this.oClasses = _oExt.oStdClasses; | |
1136 | |
1137 /* | |
1138 * Variable: bFiltered and bSorted | |
1139 * Purpose: Flag to allow callback functions to see what action has been performed | |
1140 * Scope: jQuery.dataTable.classSettings | |
1141 */ | |
1142 this.bFiltered = false; | |
1143 this.bSorted = false; | |
1144 } | |
1145 | |
1146 /* | |
1147 * Variable: oApi | |
1148 * Purpose: Container for publicly exposed 'private' functions | |
1149 * Scope: jQuery.dataTable | |
1150 */ | |
1151 this.oApi = {}; | |
1152 | |
1153 | |
1154 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * | |
1155 * Section - API functions | |
1156 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ | |
1157 | |
1158 /* | |
1159 * Function: fnDraw | |
1160 * Purpose: Redraw the table | |
1161 * Returns: - | |
1162 * Inputs: bool:bComplete - Refilter and resort (if enabled) the table before the draw. | |
1163 * Optional: default - true | |
1164 */ | |
1165 this.fnDraw = function( bComplete ) | |
1166 { | |
1167 var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] ); | |
1168 if ( typeof bComplete != 'undefined' && bComplete === false ) | |
1169 { | |
1170 _fnCalculateEnd( oSettings ); | |
1171 _fnDraw( oSettings ); | |
1172 } | |
1173 else | |
1174 { | |
1175 _fnReDraw( oSettings ); | |
1176 } | |
1177 }; | |
1178 | |
1179 /* | |
1180 * Function: fnFilter | |
1181 * Purpose: Filter the input based on data | |
1182 * Returns: - | |
1183 * Inputs: string:sInput - string to filter the table on | |
1184 * int:iColumn - optional - column to limit filtering to | |
1185 * bool:bEscapeRegex - optional - escape regex characters or not - default true | |
1186 */ | |
1187 this.fnFilter = function( sInput, iColumn, bEscapeRegex ) | |
1188 { | |
1189 var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] ); | |
1190 | |
1191 if ( typeof bEscapeRegex == 'undefined' ) | |
1192 { | |
1193 bEscapeRegex = true; | |
1194 } | |
1195 | |
1196 if ( typeof iColumn == "undefined" || iColumn === null ) | |
1197 { | |
1198 /* Global filter */ | |
1199 _fnFilterComplete( oSettings, {"sSearch":sInput, "bEscapeRegex": bEscapeRegex}, 1 ); | |
1200 } | |
1201 else | |
1202 { | |
1203 /* Single column filter */ | |
1204 oSettings.aoPreSearchCols[ iColumn ].sSearch = sInput; | |
1205 oSettings.aoPreSearchCols[ iColumn ].bEscapeRegex = bEscapeRegex; | |
1206 _fnFilterComplete( oSettings, oSettings.oPreviousSearch, 1 ); | |
1207 } | |
1208 }; | |
1209 | |
1210 /* | |
1211 * Function: fnSettings | |
1212 * Purpose: Get the settings for a particular table for extern. manipulation | |
1213 * Returns: - | |
1214 * Inputs: - | |
1215 */ | |
1216 this.fnSettings = function( nNode ) | |
1217 { | |
1218 return _fnSettingsFromNode( this[_oExt.iApiIndex] ); | |
1219 }; | |
1220 | |
1221 /* | |
1222 * Function: fnVersionCheck | |
1223 * Purpose: Check a version string against this version of DataTables. Useful for plug-ins | |
1224 * Returns: bool:true -this version of DataTables is greater or equal to the required version | |
1225 * false -this version of DataTales is not suitable | |
1226 * Inputs: string:sVersion - the version to check against. May be in the following formats: | |
1227 * "a", "a.b" or "a.b.c" | |
1228 * Notes: This function will only check the first three parts of a version string. It is | |
1229 * assumed that beta and dev versions will meet the requirements. This might change in future | |
1230 */ | |
1231 this.fnVersionCheck = function( sVersion ) | |
1232 { | |
1233 /* This is cheap, but very effective */ | |
1234 var fnZPad = function (Zpad, count) | |
1235 { | |
1236 while(Zpad.length < count) { | |
1237 Zpad += '0'; | |
1238 } | |
1239 return Zpad; | |
1240 }; | |
1241 var aThis = _oExt.sVersion.split('.'); | |
1242 var aThat = sVersion.split('.'); | |
1243 var sThis = '', sThat = ''; | |
1244 | |
1245 for ( var i=0, iLen=aThat.length ; i<iLen ; i++ ) | |
1246 { | |
1247 sThis += fnZPad( aThis[i], 3 ); | |
1248 sThat += fnZPad( aThat[i], 3 ); | |
1249 } | |
1250 | |
1251 return parseInt(sThis, 10) >= parseInt(sThat, 10); | |
1252 }; | |
1253 | |
1254 /* | |
1255 * Function: fnSort | |
1256 * Purpose: Sort the table by a particular row | |
1257 * Returns: - | |
1258 * Inputs: int:iCol - the data index to sort on. Note that this will | |
1259 * not match the 'display index' if you have hidden data entries | |
1260 */ | |
1261 this.fnSort = function( aaSort ) | |
1262 { | |
1263 var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] ); | |
1264 oSettings.aaSorting = aaSort; | |
1265 _fnSort( oSettings ); | |
1266 }; | |
1267 | |
1268 /* | |
1269 * Function: fnSortListener | |
1270 * Purpose: Attach a sort listener to an element for a given column | |
1271 * Returns: - | |
1272 * Inputs: node:nNode - the element to attach the sort listener to | |
1273 * int:iColumn - the column that a click on this node will sort on | |
1274 * function:fnCallback - callback function when sort is run - optional | |
1275 */ | |
1276 this.fnSortListener = function( nNode, iColumn, fnCallback ) | |
1277 { | |
1278 _fnSortAttachListener( _fnSettingsFromNode( this[_oExt.iApiIndex] ), nNode, iColumn, | |
1279 fnCallback ); | |
1280 }; | |
1281 | |
1282 /* | |
1283 * Function: fnAddData | |
1284 * Purpose: Add new row(s) into the table | |
1285 * Returns: array int: array of indexes (aoData) which have been added (zero length on error) | |
1286 * Inputs: array:mData - the data to be added. The length must match | |
1287 * the original data from the DOM | |
1288 * or | |
1289 * array array:mData - 2D array of data to be added | |
1290 * bool:bRedraw - redraw the table or not - default true | |
1291 * Notes: Warning - the refilter here will cause the table to redraw | |
1292 * starting at zero | |
1293 * Notes: Thanks to Yekimov Denis for contributing the basis for this function! | |
1294 */ | |
1295 this.fnAddData = function( mData, bRedraw ) | |
1296 { | |
1297 if ( mData.length === 0 ) | |
1298 { | |
1299 return []; | |
1300 } | |
1301 | |
1302 var aiReturn = []; | |
1303 var iTest; | |
1304 | |
1305 /* Find settings from table node */ | |
1306 var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] ); | |
1307 | |
1308 /* Check if we want to add multiple rows or not */ | |
1309 if ( typeof mData[0] == "object" ) | |
1310 { | |
1311 for ( var i=0 ; i<mData.length ; i++ ) | |
1312 { | |
1313 iTest = _fnAddData( oSettings, mData[i] ); | |
1314 if ( iTest == -1 ) | |
1315 { | |
1316 return aiReturn; | |
1317 } | |
1318 aiReturn.push( iTest ); | |
1319 } | |
1320 } | |
1321 else | |
1322 { | |
1323 iTest = _fnAddData( oSettings, mData ); | |
1324 if ( iTest == -1 ) | |
1325 { | |
1326 return aiReturn; | |
1327 } | |
1328 aiReturn.push( iTest ); | |
1329 } | |
1330 | |
1331 oSettings.aiDisplay = oSettings.aiDisplayMaster.slice(); | |
1332 | |
1333 /* Rebuild the search */ | |
1334 _fnBuildSearchArray( oSettings, 1 ); | |
1335 | |
1336 if ( typeof bRedraw == 'undefined' || bRedraw ) | |
1337 { | |
1338 _fnReDraw( oSettings ); | |
1339 } | |
1340 return aiReturn; | |
1341 }; | |
1342 | |
1343 /* | |
1344 * Function: fnDeleteRow | |
1345 * Purpose: Remove a row for the table | |
1346 * Returns: array:aReturn - the row that was deleted | |
1347 * Inputs: mixed:mTarget - | |
1348 * int: - index of aoData to be deleted, or | |
1349 * node(TR): - TR element you want to delete | |
1350 * function:fnCallBack - callback function - default null | |
1351 * bool:bNullRow - remove the row information from aoData by setting the value to | |
1352 * null - default false | |
1353 * Notes: This function requires a little explanation - we don't actually delete the data | |
1354 * from aoData - rather we remove it's references from aiDisplayMastr and aiDisplay. This | |
1355 * in effect prevnts DataTables from drawing it (hence deleting it) - it could be restored | |
1356 * if you really wanted. The reason for this is that actually removing the aoData object | |
1357 * would mess up all the subsequent indexes in the display arrays (they could be ajusted - | |
1358 * but this appears to do what is required). | |
1359 */ | |
1360 this.fnDeleteRow = function( mTarget, fnCallBack, bNullRow ) | |
1361 { | |
1362 /* Find settings from table node */ | |
1363 var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] ); | |
1364 var i, iAODataIndex; | |
1365 | |
1366 iAODataIndex = (typeof mTarget == 'object') ? | |
1367 _fnNodeToDataIndex(oSettings, mTarget) : mTarget; | |
1368 | |
1369 /* Delete from the display master */ | |
1370 for ( i=0 ; i<oSettings.aiDisplayMaster.length ; i++ ) | |
1371 { | |
1372 if ( oSettings.aiDisplayMaster[i] == iAODataIndex ) | |
1373 { | |
1374 oSettings.aiDisplayMaster.splice( i, 1 ); | |
1375 break; | |
1376 } | |
1377 } | |
1378 | |
1379 /* Delete from the current display index */ | |
1380 for ( i=0 ; i<oSettings.aiDisplay.length ; i++ ) | |
1381 { | |
1382 if ( oSettings.aiDisplay[i] == iAODataIndex ) | |
1383 { | |
1384 oSettings.aiDisplay.splice( i, 1 ); | |
1385 break; | |
1386 } | |
1387 } | |
1388 | |
1389 /* Rebuild the search */ | |
1390 _fnBuildSearchArray( oSettings, 1 ); | |
1391 | |
1392 /* If there is a user callback function - call it */ | |
1393 if ( typeof fnCallBack == "function" ) | |
1394 { | |
1395 fnCallBack.call( this ); | |
1396 } | |
1397 | |
1398 /* Check for an 'overflow' they case for dislaying the table */ | |
1399 if ( oSettings._iDisplayStart >= oSettings.aiDisplay.length ) | |
1400 { | |
1401 oSettings._iDisplayStart -= oSettings._iDisplayLength; | |
1402 if ( oSettings._iDisplayStart < 0 ) | |
1403 { | |
1404 oSettings._iDisplayStart = 0; | |
1405 } | |
1406 } | |
1407 | |
1408 _fnCalculateEnd( oSettings ); | |
1409 _fnDraw( oSettings ); | |
1410 | |
1411 /* Return the data array from this row */ | |
1412 var aData = oSettings.aoData[iAODataIndex]._aData.slice(); | |
1413 | |
1414 if ( typeof bNullRow != "undefined" && bNullRow === true ) | |
1415 { | |
1416 oSettings.aoData[iAODataIndex] = null; | |
1417 } | |
1418 | |
1419 return aData; | |
1420 }; | |
1421 | |
1422 /* | |
1423 * Function: fnClearTable | |
1424 * Purpose: Quickly and simply clear a table | |
1425 * Returns: - | |
1426 * Inputs: bool:bRedraw - redraw the table or not - default true | |
1427 * Notes: Thanks to Yekimov Denis for contributing the basis for this function! | |
1428 */ | |
1429 this.fnClearTable = function( bRedraw ) | |
1430 { | |
1431 /* Find settings from table node */ | |
1432 var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] ); | |
1433 _fnClearTable( oSettings ); | |
1434 | |
1435 if ( typeof bRedraw == 'undefined' || bRedraw ) | |
1436 { | |
1437 _fnDraw( oSettings ); | |
1438 } | |
1439 }; | |
1440 | |
1441 /* | |
1442 * Function: fnOpen | |
1443 * Purpose: Open a display row (append a row after the row in question) | |
1444 * Returns: node:nNewRow - the row opened | |
1445 * Inputs: node:nTr - the table row to 'open' | |
1446 * string:sHtml - the HTML to put into the row | |
1447 * string:sClass - class to give the new cell | |
1448 */ | |
1449 this.fnOpen = function( nTr, sHtml, sClass ) | |
1450 { | |
1451 /* Find settings from table node */ | |
1452 var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] ); | |
1453 | |
1454 /* the old open one if there is one */ | |
1455 this.fnClose( nTr ); | |
1456 | |
1457 var nNewRow = document.createElement("tr"); | |
1458 var nNewCell = document.createElement("td"); | |
1459 nNewRow.appendChild( nNewCell ); | |
1460 nNewCell.className = sClass; | |
1461 nNewCell.colSpan = _fnVisbleColumns( oSettings ); | |
1462 nNewCell.innerHTML = sHtml; | |
1463 | |
1464 /* If the nTr isn't on the page at the moment - then we don't insert at the moment */ | |
1465 var nTrs = $('tbody tr', oSettings.nTable); | |
1466 if ( $.inArray(nTr, nTrs) != -1 ) | |
1467 { | |
1468 $(nNewRow).insertAfter(nTr); | |
1469 } | |
1470 | |
1471 /* No point in storing the row if using server-side processing since the nParent will be | |
1472 * nuked on a re-draw anyway | |
1473 */ | |
1474 if ( !oSettings.oFeatures.bServerSide ) | |
1475 { | |
1476 oSettings.aoOpenRows.push( { | |
1477 "nTr": nNewRow, | |
1478 "nParent": nTr | |
1479 } ); | |
1480 } | |
1481 | |
1482 return nNewRow; | |
1483 }; | |
1484 | |
1485 /* | |
1486 * Function: fnClose | |
1487 * Purpose: Close a display row | |
1488 * Returns: int: 0 (success) or 1 (failed) | |
1489 * Inputs: node:nTr - the table row to 'close' | |
1490 */ | |
1491 this.fnClose = function( nTr ) | |
1492 { | |
1493 /* Find settings from table node */ | |
1494 var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] ); | |
1495 | |
1496 for ( var i=0 ; i<oSettings.aoOpenRows.length ; i++ ) | |
1497 { | |
1498 if ( oSettings.aoOpenRows[i].nParent == nTr ) | |
1499 { | |
1500 var nTrParent = oSettings.aoOpenRows[i].nTr.parentNode; | |
1501 if ( nTrParent ) | |
1502 { | |
1503 /* Remove it if it is currently on display */ | |
1504 nTrParent.removeChild( oSettings.aoOpenRows[i].nTr ); | |
1505 } | |
1506 oSettings.aoOpenRows.splice( i, 1 ); | |
1507 return 0; | |
1508 } | |
1509 } | |
1510 return 1; | |
1511 }; | |
1512 | |
1513 /* | |
1514 * Function: fnGetData | |
1515 * Purpose: Return an array with the data which is used to make up the table | |
1516 * Returns: array array string: 2d data array ([row][column]) or array string: 1d data array | |
1517 * or | |
1518 * array string (if iRow specified) | |
1519 * Inputs: mixed:mRow - optional - if not present, then the full 2D array for the table | |
1520 * if given then: | |
1521 * int: - return 1D array for aoData entry of this index | |
1522 * node(TR): - return 1D array for this TR element | |
1523 * Inputs: int:iRow - optional - if present then the array returned will be the data for | |
1524 * the row with the index 'iRow' | |
1525 */ | |
1526 this.fnGetData = function( mRow ) | |
1527 { | |
1528 var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] ); | |
1529 | |
1530 if ( typeof mRow != 'undefined' ) | |
1531 { | |
1532 var iRow = (typeof mRow == 'object') ? | |
1533 _fnNodeToDataIndex(oSettings, mRow) : mRow; | |
1534 return oSettings.aoData[iRow]._aData; | |
1535 } | |
1536 return _fnGetDataMaster( oSettings ); | |
1537 }; | |
1538 | |
1539 /* | |
1540 * Function: fnGetNodes | |
1541 * Purpose: Return an array with the TR nodes used for drawing the table | |
1542 * Returns: array node: TR elements | |
1543 * or | |
1544 * node (if iRow specified) | |
1545 * Inputs: int:iRow - optional - if present then the array returned will be the node for | |
1546 * the row with the index 'iRow' | |
1547 */ | |
1548 this.fnGetNodes = function( iRow ) | |
1549 { | |
1550 var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] ); | |
1551 | |
1552 if ( typeof iRow != 'undefined' ) | |
1553 { | |
1554 return oSettings.aoData[iRow].nTr; | |
1555 } | |
1556 return _fnGetTrNodes( oSettings ); | |
1557 }; | |
1558 | |
1559 /* | |
1560 * Function: fnGetPosition | |
1561 * Purpose: Get the array indexes of a particular cell from it's DOM element | |
1562 * Returns: int: - row index, or array[ int, int, int ]: - row index, column index (visible) | |
1563 * and column index including hidden columns | |
1564 * Inputs: node:nNode - this can either be a TR or a TD in the table, the return is | |
1565 * dependent on this input | |
1566 */ | |
1567 this.fnGetPosition = function( nNode ) | |
1568 { | |
1569 var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] ); | |
1570 var i; | |
1571 | |
1572 if ( nNode.nodeName == "TR" ) | |
1573 { | |
1574 return _fnNodeToDataIndex(oSettings, nNode); | |
1575 } | |
1576 else if ( nNode.nodeName == "TD" ) | |
1577 { | |
1578 var iDataIndex = _fnNodeToDataIndex(oSettings, nNode.parentNode); | |
1579 var iCorrector = 0; | |
1580 for ( var j=0 ; j<oSettings.aoColumns.length ; j++ ) | |
1581 { | |
1582 if ( oSettings.aoColumns[j].bVisible ) | |
1583 { | |
1584 if ( oSettings.aoData[iDataIndex].nTr.getElementsByTagName('td')[j-iCorrector] == nNode ) | |
1585 { | |
1586 return [ iDataIndex, j-iCorrector, j ]; | |
1587 } | |
1588 } | |
1589 else | |
1590 { | |
1591 iCorrector++; | |
1592 } | |
1593 } | |
1594 } | |
1595 return null; | |
1596 }; | |
1597 | |
1598 /* | |
1599 * Function: fnUpdate | |
1600 * Purpose: Update a table cell or row | |
1601 * Returns: int: 0 okay, 1 error | |
1602 * Inputs: array string 'or' string:mData - data to update the cell/row with | |
1603 * mixed:mRow - | |
1604 * int: - index of aoData to be updated, or | |
1605 * node(TR): - TR element you want to update | |
1606 * int:iColumn - the column to update - optional (not used of mData is 2D) | |
1607 * bool:bRedraw - redraw the table or not - default true | |
1608 */ | |
1609 this.fnUpdate = function( mData, mRow, iColumn, bRedraw ) | |
1610 { | |
1611 var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] ); | |
1612 var iVisibleColumn; | |
1613 var sDisplay; | |
1614 var iRow = (typeof mRow == 'object') ? | |
1615 _fnNodeToDataIndex(oSettings, mRow) : mRow; | |
1616 | |
1617 if ( typeof mData != 'object' ) | |
1618 { | |
1619 sDisplay = mData; | |
1620 oSettings.aoData[iRow]._aData[iColumn] = sDisplay; | |
1621 | |
1622 if ( oSettings.aoColumns[iColumn].fnRender !== null ) | |
1623 { | |
1624 sDisplay = oSettings.aoColumns[iColumn].fnRender( { | |
1625 "iDataRow": iRow, | |
1626 "iDataColumn": iColumn, | |
1627 "aData": oSettings.aoData[iRow]._aData, | |
1628 "oSettings": oSettings | |
1629 } ); | |
1630 | |
1631 if ( oSettings.aoColumns[iColumn].bUseRendered ) | |
1632 { | |
1633 oSettings.aoData[iRow]._aData[iColumn] = sDisplay; | |
1634 } | |
1635 } | |
1636 | |
1637 iVisibleColumn = _fnColumnIndexToVisible( oSettings, iColumn ); | |
1638 if ( iVisibleColumn !== null ) | |
1639 { | |
1640 oSettings.aoData[iRow].nTr.getElementsByTagName('td')[iVisibleColumn].innerHTML = | |
1641 sDisplay; | |
1642 } | |
1643 } | |
1644 else | |
1645 { | |
1646 if ( mData.length != oSettings.aoColumns.length ) | |
1647 { | |
1648 alert( 'DataTables warning: An array passed to fnUpdate must have the same number of '+ | |
1649 'columns as the table in question - in this case '+oSettings.aoColumns.length ); | |
1650 return 1; | |
1651 } | |
1652 | |
1653 for ( var i=0 ; i<mData.length ; i++ ) | |
1654 { | |
1655 sDisplay = mData[i]; | |
1656 oSettings.aoData[iRow]._aData[i] = sDisplay; | |
1657 | |
1658 if ( oSettings.aoColumns[i].fnRender !== null ) | |
1659 { | |
1660 sDisplay = oSettings.aoColumns[i].fnRender( { | |
1661 "iDataRow": iRow, | |
1662 "iDataColumn": i, | |
1663 "aData": oSettings.aoData[iRow]._aData, | |
1664 "oSettings": oSettings | |
1665 } ); | |
1666 | |
1667 if ( oSettings.aoColumns[i].bUseRendered ) | |
1668 { | |
1669 oSettings.aoData[iRow]._aData[i] = sDisplay; | |
1670 } | |
1671 } | |
1672 | |
1673 iVisibleColumn = _fnColumnIndexToVisible( oSettings, i ); | |
1674 if ( iVisibleColumn !== null ) | |
1675 { | |
1676 oSettings.aoData[iRow].nTr.getElementsByTagName('td')[iVisibleColumn].innerHTML = | |
1677 sDisplay; | |
1678 } | |
1679 } | |
1680 } | |
1681 | |
1682 /* Update the search array */ | |
1683 _fnBuildSearchArray( oSettings, 1 ); | |
1684 | |
1685 /* Redraw the table */ | |
1686 if ( typeof bRedraw != 'undefined' && bRedraw ) | |
1687 { | |
1688 _fnReDraw( oSettings ); | |
1689 } | |
1690 return 0; | |
1691 }; | |
1692 | |
1693 | |
1694 /* | |
1695 * Function: fnShowColoumn | |
1696 * Purpose: Show a particular column | |
1697 * Returns: - | |
1698 * Inputs: int:iCol - the column whose display should be changed | |
1699 * bool:bShow - show (true) or hide (false) the column | |
1700 */ | |
1701 this.fnSetColumnVis = function ( iCol, bShow ) | |
1702 { | |
1703 var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] ); | |
1704 var i, iLen; | |
1705 var iColumns = oSettings.aoColumns.length; | |
1706 var nTd, anTds; | |
1707 | |
1708 /* No point in doing anything if we are requesting what is already true */ | |
1709 if ( oSettings.aoColumns[iCol].bVisible == bShow ) | |
1710 { | |
1711 return; | |
1712 } | |
1713 | |
1714 var nTrHead = $('thead:eq(0)>tr', oSettings.nTable)[0]; | |
1715 var nTrFoot = $('tfoot:eq(0)>tr', oSettings.nTable)[0]; | |
1716 var anTheadTh = []; | |
1717 var anTfootTh = []; | |
1718 for ( i=0 ; i<iColumns ; i++ ) | |
1719 { | |
1720 anTheadTh.push( oSettings.aoColumns[i].nTh ); | |
1721 anTfootTh.push( oSettings.aoColumns[i].nTf ); | |
1722 } | |
1723 | |
1724 /* Show the column */ | |
1725 if ( bShow ) | |
1726 { | |
1727 var iInsert = 0; | |
1728 for ( i=0 ; i<iCol ; i++ ) | |
1729 { | |
1730 if ( oSettings.aoColumns[i].bVisible ) | |
1731 { | |
1732 iInsert++; | |
1733 } | |
1734 } | |
1735 | |
1736 /* Need to decide if we should use appendChild or insertBefore */ | |
1737 if ( iInsert >= _fnVisbleColumns( oSettings ) ) | |
1738 { | |
1739 nTrHead.appendChild( anTheadTh[iCol] ); | |
1740 if ( nTrFoot ) | |
1741 { | |
1742 nTrFoot.appendChild( anTfootTh[iCol] ); | |
1743 } | |
1744 | |
1745 for ( i=0, iLen=oSettings.aoData.length ; i<iLen ; i++ ) | |
1746 { | |
1747 nTd = oSettings.aoData[i]._anHidden[iCol]; | |
1748 oSettings.aoData[i].nTr.appendChild( nTd ); | |
1749 } | |
1750 } | |
1751 else | |
1752 { | |
1753 /* Which coloumn should we be inserting before? */ | |
1754 var iBefore; | |
1755 for ( i=iCol ; i<iColumns ; i++ ) | |
1756 { | |
1757 iBefore = _fnColumnIndexToVisible( oSettings, i ); | |
1758 if ( iBefore !== null ) | |
1759 { | |
1760 break; | |
1761 } | |
1762 } | |
1763 | |
1764 nTrHead.insertBefore( anTheadTh[iCol], nTrHead.getElementsByTagName('th')[iBefore] ); | |
1765 if ( nTrFoot ) | |
1766 { | |
1767 nTrFoot.insertBefore( anTfootTh[iCol], nTrFoot.getElementsByTagName('th')[iBefore] ); | |
1768 } | |
1769 | |
1770 anTds = _fnGetTdNodes( oSettings ); | |
1771 for ( i=0, iLen=oSettings.aoData.length ; i<iLen ; i++ ) | |
1772 { | |
1773 nTd = oSettings.aoData[i]._anHidden[iCol]; | |
1774 oSettings.aoData[i].nTr.insertBefore( nTd, $('>td:eq('+iBefore+')', | |
1775 oSettings.aoData[i].nTr)[0] ); | |
1776 } | |
1777 } | |
1778 | |
1779 oSettings.aoColumns[iCol].bVisible = true; | |
1780 } | |
1781 else | |
1782 { | |
1783 /* Remove a column from display */ | |
1784 nTrHead.removeChild( anTheadTh[iCol] ); | |
1785 if ( nTrFoot ) | |
1786 { | |
1787 nTrFoot.removeChild( anTfootTh[iCol] ); | |
1788 } | |
1789 | |
1790 anTds = _fnGetTdNodes( oSettings ); | |
1791 for ( i=0, iLen=oSettings.aoData.length ; i<iLen ; i++ ) | |
1792 { | |
1793 nTd = anTds[ ( i*oSettings.aoColumns.length) + iCol ]; | |
1794 oSettings.aoData[i]._anHidden[iCol] = nTd; | |
1795 nTd.parentNode.removeChild( nTd ); | |
1796 } | |
1797 | |
1798 oSettings.aoColumns[iCol].bVisible = false; | |
1799 } | |
1800 | |
1801 /* If there are any 'open' rows, then we need to alter the colspan for this col change */ | |
1802 for ( i=0, iLen=oSettings.aoOpenRows.length ; i<iLen ; i++ ) | |
1803 { | |
1804 oSettings.aoOpenRows[i].nTr.colSpan = _fnVisbleColumns( oSettings ); | |
1805 } | |
1806 | |
1807 /* Since there is no redraw done here, we need to save the state manually */ | |
1808 _fnSaveState( oSettings ); | |
1809 }; | |
1810 | |
1811 /* | |
1812 * Function: fnPageChange | |
1813 * Purpose: Change the pagination | |
1814 * Returns: - | |
1815 * Inputs: string:sAction - paging action to take: "first", "previous", "next" or "last" | |
1816 * bool:bRedraw - redraw the table or not - optional - default true | |
1817 */ | |
1818 this.fnPageChange = function ( sAction, bRedraw ) | |
1819 { | |
1820 var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] ); | |
1821 _fnPageChange( oSettings, sAction ); | |
1822 _fnCalculateEnd( oSettings ); | |
1823 | |
1824 if ( typeof bRedraw == 'undefined' || bRedraw ) | |
1825 { | |
1826 _fnDraw( oSettings ); | |
1827 } | |
1828 }; | |
1829 | |
1830 | |
1831 /* | |
1832 * Plugin API functions | |
1833 * | |
1834 * This call will add the functions which are defined in _oExt.oApi to the | |
1835 * DataTables object, providing a rather nice way to allow plug-in API functions. Note that | |
1836 * this is done here, so that API function can actually override the built in API functions if | |
1837 * required for a particular purpose. | |
1838 */ | |
1839 | |
1840 /* | |
1841 * Function: _fnExternApiFunc | |
1842 * Purpose: Create a wrapper function for exporting an internal func to an external API func | |
1843 * Returns: function: - wrapped function | |
1844 * Inputs: string:sFunc - API function name | |
1845 */ | |
1846 function _fnExternApiFunc (sFunc) | |
1847 { | |
1848 return function() { | |
1849 var aArgs = [_fnSettingsFromNode(this[_oExt.iApiIndex])].concat( | |
1850 Array.prototype.slice.call(arguments) ); | |
1851 return _oExt.oApi[sFunc].apply( this, aArgs ); | |
1852 }; | |
1853 } | |
1854 | |
1855 for ( var sFunc in _oExt.oApi ) | |
1856 { | |
1857 if ( sFunc ) | |
1858 { | |
1859 /* | |
1860 * Function: anon | |
1861 * Purpose: Wrap the plug-in API functions in order to provide the settings as 1st arg | |
1862 * and execute in this scope | |
1863 * Returns: - | |
1864 * Inputs: - | |
1865 */ | |
1866 this[sFunc] = _fnExternApiFunc(sFunc); | |
1867 } | |
1868 } | |
1869 | |
1870 | |
1871 | |
1872 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * | |
1873 * Section - Local functions | |
1874 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ | |
1875 | |
1876 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * | |
1877 * Section - Initalisation | |
1878 */ | |
1879 | |
1880 /* | |
1881 * Function: _fnInitalise | |
1882 * Purpose: Draw the table for the first time, adding all required features | |
1883 * Returns: - | |
1884 * Inputs: object:oSettings - dataTables settings object | |
1885 */ | |
1886 function _fnInitalise ( oSettings ) | |
1887 { | |
1888 /* Ensure that the table data is fully initialised */ | |
1889 if ( oSettings.bInitialised === false ) | |
1890 { | |
1891 setTimeout( function(){ _fnInitalise( oSettings ); }, 200 ); | |
1892 return; | |
1893 } | |
1894 | |
1895 /* Show the display HTML options */ | |
1896 _fnAddOptionsHtml( oSettings ); | |
1897 | |
1898 /* Draw the headers for the table */ | |
1899 _fnDrawHead( oSettings ); | |
1900 | |
1901 /* If there is default sorting required - let's do it. The sort function | |
1902 * will do the drawing for us. Otherwise we draw the table | |
1903 */ | |
1904 if ( oSettings.oFeatures.bSort ) | |
1905 { | |
1906 _fnSort( oSettings, false ); | |
1907 /* | |
1908 * Add the sorting classes to the header and the body (if needed). | |
1909 * Reason for doing it here after the first draw is to stop classes being applied to the | |
1910 * 'static' table. | |
1911 */ | |
1912 _fnSortingClasses( oSettings ); | |
1913 } | |
1914 else | |
1915 { | |
1916 oSettings.aiDisplay = oSettings.aiDisplayMaster.slice(); | |
1917 _fnCalculateEnd( oSettings ); | |
1918 _fnDraw( oSettings ); | |
1919 } | |
1920 | |
1921 /* if there is an ajax source */ | |
1922 if ( oSettings.sAjaxSource !== null && !oSettings.oFeatures.bServerSide ) | |
1923 { | |
1924 _fnProcessingDisplay( oSettings, true ); | |
1925 | |
1926 oSettings.fnServerData( oSettings.sAjaxSource, null, function(json) { | |
1927 | |
1928 /* Got the data - add it to the table */ | |
1929 for ( var i=0 ; i<json.aaData.length ; i++ ) | |
1930 { | |
1931 _fnAddData( oSettings, json.aaData[i] ); | |
1932 } | |
1933 | |
1934 /* Reset the init display for cookie saving. We've already done a filter, and | |
1935 * therefore cleared it before. So we need to make it appear 'fresh' | |
1936 */ | |
1937 oSettings.iInitDisplayStart = oSettings._iDisplayStart; | |
1938 | |
1939 if ( oSettings.oFeatures.bSort ) | |
1940 { | |
1941 _fnSort( oSettings ); | |
1942 } | |
1943 else | |
1944 { | |
1945 oSettings.aiDisplay = oSettings.aiDisplayMaster.slice(); | |
1946 _fnCalculateEnd( oSettings ); | |
1947 _fnDraw( oSettings ); | |
1948 } | |
1949 | |
1950 _fnProcessingDisplay( oSettings, false ); | |
1951 | |
1952 /* Run the init callback if there is one */ | |
1953 if ( typeof oSettings.fnInitComplete == 'function' ) | |
1954 { | |
1955 oSettings.fnInitComplete( oSettings, json ); | |
1956 } | |
1957 } ); | |
1958 return; | |
1959 } | |
1960 | |
1961 /* Run the init callback if there is one */ | |
1962 if ( typeof oSettings.fnInitComplete == 'function' ) | |
1963 { | |
1964 oSettings.fnInitComplete( oSettings ); | |
1965 } | |
1966 | |
1967 if ( !oSettings.oFeatures.bServerSide ) | |
1968 { | |
1969 _fnProcessingDisplay( oSettings, false ); | |
1970 } | |
1971 } | |
1972 | |
1973 /* | |
1974 * Function: _fnLanguageProcess | |
1975 * Purpose: Copy language variables from remote object to a local one | |
1976 * Returns: - | |
1977 * Inputs: object:oSettings - dataTables settings object | |
1978 * object:oLanguage - Language information | |
1979 * bool:bInit - init once complete | |
1980 */ | |
1981 function _fnLanguageProcess( oSettings, oLanguage, bInit ) | |
1982 { | |
1983 _fnMap( oSettings.oLanguage, oLanguage, 'sProcessing' ); | |
1984 _fnMap( oSettings.oLanguage, oLanguage, 'sLengthMenu' ); | |
1985 _fnMap( oSettings.oLanguage, oLanguage, 'sZeroRecords' ); | |
1986 _fnMap( oSettings.oLanguage, oLanguage, 'sInfo' ); | |
1987 _fnMap( oSettings.oLanguage, oLanguage, 'sInfoEmpty' ); | |
1988 _fnMap( oSettings.oLanguage, oLanguage, 'sInfoFiltered' ); | |
1989 _fnMap( oSettings.oLanguage, oLanguage, 'sInfoPostFix' ); | |
1990 _fnMap( oSettings.oLanguage, oLanguage, 'sSearch' ); | |
1991 | |
1992 if ( typeof oLanguage.oPaginate != 'undefined' ) | |
1993 { | |
1994 _fnMap( oSettings.oLanguage.oPaginate, oLanguage.oPaginate, 'sFirst' ); | |
1995 _fnMap( oSettings.oLanguage.oPaginate, oLanguage.oPaginate, 'sPrevious' ); | |
1996 _fnMap( oSettings.oLanguage.oPaginate, oLanguage.oPaginate, 'sNext' ); | |
1997 _fnMap( oSettings.oLanguage.oPaginate, oLanguage.oPaginate, 'sLast' ); | |
1998 } | |
1999 | |
2000 if ( bInit ) | |
2001 { | |
2002 _fnInitalise( oSettings ); | |
2003 } | |
2004 } | |
2005 | |
2006 /* | |
2007 * Function: _fnAddColumn | |
2008 * Purpose: Add a column to the list used for the table | |
2009 * Returns: - | |
2010 * Inputs: object:oSettings - dataTables settings object | |
2011 * object:oOptions - object with sType, bVisible and bSearchable | |
2012 * node:nTh - the th element for this column | |
2013 * Notes: All options in enter column can be over-ridden by the user | |
2014 * initialisation of dataTables | |
2015 */ | |
2016 function _fnAddColumn( oSettings, oOptions, nTh ) | |
2017 { | |
2018 oSettings.aoColumns[ oSettings.aoColumns.length++ ] = { | |
2019 "sType": null, | |
2020 "_bAutoType": true, | |
2021 "bVisible": true, | |
2022 "bSearchable": true, | |
2023 "bSortable": true, | |
2024 "asSorting": [ 'asc', 'desc' ], | |
2025 "sSortingClass": oSettings.oClasses.sSortable, | |
2026 "sSortingClassJUI": oSettings.oClasses.sSortJUI, | |
2027 "sTitle": nTh ? nTh.innerHTML : '', | |
2028 "sName": '', | |
2029 "sWidth": null, | |
2030 "sClass": null, | |
2031 "fnRender": null, | |
2032 "bUseRendered": true, | |
2033 "iDataSort": oSettings.aoColumns.length-1, | |
2034 "sSortDataType": 'std', | |
2035 "nTh": nTh ? nTh : document.createElement('th'), | |
2036 "nTf": null | |
2037 }; | |
2038 | |
2039 var iLength = oSettings.aoColumns.length-1; | |
2040 var oCol = oSettings.aoColumns[ iLength ]; | |
2041 | |
2042 /* User specified column options */ | |
2043 if ( typeof oOptions != 'undefined' && oOptions !== null ) | |
2044 { | |
2045 if ( typeof oOptions.sType != 'undefined' ) | |
2046 { | |
2047 oCol.sType = oOptions.sType; | |
2048 oCol._bAutoType = false; | |
2049 } | |
2050 | |
2051 _fnMap( oCol, oOptions, "bVisible" ); | |
2052 _fnMap( oCol, oOptions, "bSearchable" ); | |
2053 _fnMap( oCol, oOptions, "bSortable" ); | |
2054 _fnMap( oCol, oOptions, "sTitle" ); | |
2055 _fnMap( oCol, oOptions, "sName" ); | |
2056 _fnMap( oCol, oOptions, "sWidth" ); | |
2057 _fnMap( oCol, oOptions, "sClass" ); | |
2058 _fnMap( oCol, oOptions, "fnRender" ); | |
2059 _fnMap( oCol, oOptions, "bUseRendered" ); | |
2060 _fnMap( oCol, oOptions, "iDataSort" ); | |
2061 _fnMap( oCol, oOptions, "asSorting" ); | |
2062 _fnMap( oCol, oOptions, "sSortDataType" ); | |
2063 } | |
2064 | |
2065 /* Feature sorting overrides column specific when off */ | |
2066 if ( !oSettings.oFeatures.bSort ) | |
2067 { | |
2068 oCol.bSortable = false; | |
2069 } | |
2070 | |
2071 /* Check that the class assignment is correct for sorting */ | |
2072 if ( !oCol.bSortable || | |
2073 ($.inArray('asc', oCol.asSorting) == -1 && $.inArray('desc', oCol.asSorting) == -1) ) | |
2074 { | |
2075 oCol.sSortingClass = oSettings.oClasses.sSortableNone; | |
2076 oCol.sSortingClassJUI = ""; | |
2077 } | |
2078 else if ( $.inArray('asc', oCol.asSorting) != -1 && $.inArray('desc', oCol.asSorting) == -1 ) | |
2079 { | |
2080 oCol.sSortingClass = oSettings.oClasses.sSortableAsc; | |
2081 oCol.sSortingClassJUI = oSettings.oClasses.sSortJUIAscAllowed; | |
2082 } | |
2083 else if ( $.inArray('asc', oCol.asSorting) == -1 && $.inArray('desc', oCol.asSorting) != -1 ) | |
2084 { | |
2085 oCol.sSortingClass = oSettings.oClasses.sSortableDesc; | |
2086 oCol.sSortingClassJUI = oSettings.oClasses.sSortJUIDescAllowed; | |
2087 } | |
2088 | |
2089 /* Add a column specific filter */ | |
2090 if ( typeof oSettings.aoPreSearchCols[ iLength ] == 'undefined' || | |
2091 oSettings.aoPreSearchCols[ iLength ] === null ) | |
2092 { | |
2093 oSettings.aoPreSearchCols[ iLength ] = { | |
2094 "sSearch": "", | |
2095 "bEscapeRegex": true | |
2096 }; | |
2097 } | |
2098 else if ( typeof oSettings.aoPreSearchCols[ iLength ].bEscapeRegex == 'undefined' ) | |
2099 { | |
2100 /* Don't require that the user must specify bEscapeRegex */ | |
2101 oSettings.aoPreSearchCols[ iLength ].bEscapeRegex = true; | |
2102 } | |
2103 } | |
2104 | |
2105 /* | |
2106 * Function: _fnAddData | |
2107 * Purpose: Add a data array to the table, creating DOM node etc | |
2108 * Returns: int: - >=0 if successful (index of new aoData entry), -1 if failed | |
2109 * Inputs: object:oSettings - dataTables settings object | |
2110 * array:aData - data array to be added | |
2111 */ | |
2112 function _fnAddData ( oSettings, aData ) | |
2113 { | |
2114 /* Sanity check the length of the new array */ | |
2115 if ( aData.length != oSettings.aoColumns.length ) | |
2116 { | |
2117 alert( "DataTables warning: Added data does not match known number of columns" ); | |
2118 return -1; | |
2119 } | |
2120 | |
2121 /* Create the object for storing information about this new row */ | |
2122 var iThisIndex = oSettings.aoData.length; | |
2123 oSettings.aoData.push( { | |
2124 "nTr": document.createElement('tr'), | |
2125 "_iId": oSettings.iNextId++, | |
2126 "_aData": aData.slice(), | |
2127 "_anHidden": [], | |
2128 "_sRowStripe": '' | |
2129 } ); | |
2130 | |
2131 /* Create the cells */ | |
2132 var nTd, sThisType; | |
2133 for ( var i=0 ; i<aData.length ; i++ ) | |
2134 { | |
2135 nTd = document.createElement('td'); | |
2136 | |
2137 if ( typeof oSettings.aoColumns[i].fnRender == 'function' ) | |
2138 { | |
2139 var sRendered = oSettings.aoColumns[i].fnRender( { | |
2140 "iDataRow": iThisIndex, | |
2141 "iDataColumn": i, | |
2142 "aData": aData, | |
2143 "oSettings": oSettings | |
2144 } ); | |
2145 nTd.innerHTML = sRendered; | |
2146 if ( oSettings.aoColumns[i].bUseRendered ) | |
2147 { | |
2148 /* Use the rendered data for filtering/sorting */ | |
2149 oSettings.aoData[iThisIndex]._aData[i] = sRendered; | |
2150 } | |
2151 } | |
2152 else | |
2153 { | |
2154 nTd.innerHTML = aData[i]; | |
2155 } | |
2156 | |
2157 if ( oSettings.aoColumns[i].sClass !== null ) | |
2158 { | |
2159 nTd.className = oSettings.aoColumns[i].sClass; | |
2160 } | |
2161 | |
2162 /* See if we should auto-detect the column type */ | |
2163 if ( oSettings.aoColumns[i]._bAutoType && oSettings.aoColumns[i].sType != 'string' ) | |
2164 { | |
2165 /* Attempt to auto detect the type - same as _fnGatherData() */ | |
2166 sThisType = _fnDetectType( oSettings.aoData[iThisIndex]._aData[i] ); | |
2167 if ( oSettings.aoColumns[i].sType === null ) | |
2168 { | |
2169 oSettings.aoColumns[i].sType = sThisType; | |
2170 } | |
2171 else if ( oSettings.aoColumns[i].sType != sThisType ) | |
2172 { | |
2173 /* String is always the 'fallback' option */ | |
2174 oSettings.aoColumns[i].sType = 'string'; | |
2175 } | |
2176 } | |
2177 | |
2178 if ( oSettings.aoColumns[i].bVisible ) | |
2179 { | |
2180 oSettings.aoData[iThisIndex].nTr.appendChild( nTd ); | |
2181 } | |
2182 else | |
2183 { | |
2184 oSettings.aoData[iThisIndex]._anHidden[i] = nTd; | |
2185 } | |
2186 } | |
2187 | |
2188 /* Add to the display array */ | |
2189 oSettings.aiDisplayMaster.push( iThisIndex ); | |
2190 return iThisIndex; | |
2191 } | |
2192 | |
2193 /* | |
2194 * Function: _fnGatherData | |
2195 * Purpose: Read in the data from the target table | |
2196 * Returns: - | |
2197 * Inputs: object:oSettings - dataTables settings object | |
2198 */ | |
2199 function _fnGatherData( oSettings ) | |
2200 { | |
2201 var iLoop, i, iLen, j, jLen, jInner, | |
2202 nTds, nTrs, nTd, aLocalData, iThisIndex, | |
2203 iRow, iRows, iColumn, iColumns; | |
2204 | |
2205 /* | |
2206 * Process by row first | |
2207 * Add the data object for the whole table - storing the tr node. Note - no point in getting | |
2208 * DOM based data if we are going to go and replace it with Ajax source data. | |
2209 */ | |
2210 if ( oSettings.sAjaxSource === null ) | |
2211 { | |
2212 nTrs = oSettings.nTable.getElementsByTagName('tbody')[0].childNodes; | |
2213 for ( i=0, iLen=nTrs.length ; i<iLen ; i++ ) | |
2214 { | |
2215 if ( nTrs[i].nodeName == "TR" ) | |
2216 { | |
2217 iThisIndex = oSettings.aoData.length; | |
2218 oSettings.aoData.push( { | |
2219 "nTr": nTrs[i], | |
2220 "_iId": oSettings.iNextId++, | |
2221 "_aData": [], | |
2222 "_anHidden": [], | |
2223 "_sRowStripe": '' | |
2224 } ); | |
2225 | |
2226 oSettings.aiDisplayMaster.push( iThisIndex ); | |
2227 | |
2228 aLocalData = oSettings.aoData[iThisIndex]._aData; | |
2229 nTds = nTrs[i].childNodes; | |
2230 jInner = 0; | |
2231 | |
2232 for ( j=0, jLen=nTds.length ; j<jLen ; j++ ) | |
2233 { | |
2234 if ( nTds[j].nodeName == "TD" ) | |
2235 { | |
2236 aLocalData[jInner] = nTds[j].innerHTML; | |
2237 jInner++; | |
2238 } | |
2239 } | |
2240 } | |
2241 } | |
2242 } | |
2243 | |
2244 /* Gather in the TD elements of the Table - note that this is basically the same as | |
2245 * fnGetTdNodes, but that function takes account of hidden columns, which we haven't yet | |
2246 * setup! | |
2247 */ | |
2248 nTrs = _fnGetTrNodes( oSettings ); | |
2249 nTds = []; | |
2250 for ( i=0, iLen=nTrs.length ; i<iLen ; i++ ) | |
2251 { | |
2252 for ( j=0, jLen=nTrs[i].childNodes.length ; j<jLen ; j++ ) | |
2253 { | |
2254 nTd = nTrs[i].childNodes[j]; | |
2255 if ( nTd.nodeName == "TD" ) | |
2256 { | |
2257 nTds.push( nTd ); | |
2258 } | |
2259 } | |
2260 } | |
2261 | |
2262 /* Sanity check */ | |
2263 if ( nTds.length != nTrs.length * oSettings.aoColumns.length ) | |
2264 { | |
2265 alert( "DataTables warning: Unexpected number of TD elements. Expected "+ | |
2266 (nTrs.length * oSettings.aoColumns.length)+" and got "+nTds.length+". DataTables does "+ | |
2267 "not support rowspan / colspan in the table body, and there must be one cell for each "+ | |
2268 "row/column combination." ); | |
2269 } | |
2270 | |
2271 /* Now process by column */ | |
2272 for ( iColumn=0, iColumns=oSettings.aoColumns.length ; iColumn<iColumns ; iColumn++ ) | |
2273 { | |
2274 /* Get the title of the column - unless there is a user set one */ | |
2275 if ( oSettings.aoColumns[iColumn].sTitle === null ) | |
2276 { | |
2277 oSettings.aoColumns[iColumn].sTitle = oSettings.aoColumns[iColumn].nTh.innerHTML; | |
2278 } | |
2279 | |
2280 var | |
2281 bAutoType = oSettings.aoColumns[iColumn]._bAutoType, | |
2282 bRender = typeof oSettings.aoColumns[iColumn].fnRender == 'function', | |
2283 bClass = oSettings.aoColumns[iColumn].sClass !== null, | |
2284 bVisible = oSettings.aoColumns[iColumn].bVisible, | |
2285 nCell, sThisType, sRendered; | |
2286 | |
2287 /* A single loop to rule them all (and be more efficient) */ | |
2288 if ( bAutoType || bRender || bClass || !bVisible ) | |
2289 { | |
2290 for ( iRow=0, iRows=oSettings.aoData.length ; iRow<iRows ; iRow++ ) | |
2291 { | |
2292 nCell = nTds[ (iRow*iColumns) + iColumn ]; | |
2293 | |
2294 /* Type detection */ | |
2295 if ( bAutoType ) | |
2296 { | |
2297 if ( oSettings.aoColumns[iColumn].sType != 'string' ) | |
2298 { | |
2299 sThisType = _fnDetectType( oSettings.aoData[iRow]._aData[iColumn] ); | |
2300 if ( oSettings.aoColumns[iColumn].sType === null ) | |
2301 { | |
2302 oSettings.aoColumns[iColumn].sType = sThisType; | |
2303 } | |
2304 else if ( oSettings.aoColumns[iColumn].sType != sThisType ) | |
2305 { | |
2306 /* String is always the 'fallback' option */ | |
2307 oSettings.aoColumns[iColumn].sType = 'string'; | |
2308 } | |
2309 } | |
2310 } | |
2311 | |
2312 /* Rendering */ | |
2313 if ( bRender ) | |
2314 { | |
2315 sRendered = oSettings.aoColumns[iColumn].fnRender( { | |
2316 "iDataRow": iRow, | |
2317 "iDataColumn": iColumn, | |
2318 "aData": oSettings.aoData[iRow]._aData, | |
2319 "oSettings": oSettings | |
2320 } ); | |
2321 nCell.innerHTML = sRendered; | |
2322 if ( oSettings.aoColumns[iColumn].bUseRendered ) | |
2323 { | |
2324 /* Use the rendered data for filtering/sorting */ | |
2325 oSettings.aoData[iRow]._aData[iColumn] = sRendered; | |
2326 } | |
2327 } | |
2328 | |
2329 /* Classes */ | |
2330 if ( bClass ) | |
2331 { | |
2332 nCell.className += ' '+oSettings.aoColumns[iColumn].sClass; | |
2333 } | |
2334 | |
2335 /* Column visability */ | |
2336 if ( !bVisible ) | |
2337 { | |
2338 oSettings.aoData[iRow]._anHidden[iColumn] = nCell; | |
2339 nCell.parentNode.removeChild( nCell ); | |
2340 } | |
2341 } | |
2342 } | |
2343 } | |
2344 } | |
2345 | |
2346 | |
2347 | |
2348 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * | |
2349 * Section - Drawing functions | |
2350 */ | |
2351 | |
2352 /* | |
2353 * Function: _fnDrawHead | |
2354 * Purpose: Create the HTML header for the table | |
2355 * Returns: - | |
2356 * Inputs: object:oSettings - dataTables settings object | |
2357 */ | |
2358 function _fnDrawHead( oSettings ) | |
2359 { | |
2360 var i, nTh, iLen; | |
2361 var iThs = oSettings.nTable.getElementsByTagName('thead')[0].getElementsByTagName('th').length; | |
2362 var iCorrector = 0; | |
2363 | |
2364 /* If there is a header in place - then use it - otherwise it's going to get nuked... */ | |
2365 if ( iThs !== 0 ) | |
2366 { | |
2367 /* We've got a thead from the DOM, so remove hidden columns and apply width to vis cols */ | |
2368 for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ ) | |
2369 { | |
2370 //oSettings.aoColumns[i].nTh = nThs[i]; | |
2371 nTh = oSettings.aoColumns[i].nTh; | |
2372 | |
2373 if ( oSettings.aoColumns[i].bVisible ) | |
2374 { | |
2375 /* Set width */ | |
2376 if ( oSettings.aoColumns[i].sWidth !== null ) | |
2377 { | |
2378 nTh.style.width = oSettings.aoColumns[i].sWidth; | |
2379 } | |
2380 | |
2381 /* Set the title of the column if it is user defined (not what was auto detected) */ | |
2382 if ( oSettings.aoColumns[i].sTitle != nTh.innerHTML ) | |
2383 { | |
2384 nTh.innerHTML = oSettings.aoColumns[i].sTitle; | |
2385 } | |
2386 } | |
2387 else | |
2388 { | |
2389 nTh.parentNode.removeChild( nTh ); | |
2390 iCorrector++; | |
2391 } | |
2392 } | |
2393 } | |
2394 else | |
2395 { | |
2396 /* We don't have a header in the DOM - so we are going to have to create one */ | |
2397 var nTr = document.createElement( "tr" ); | |
2398 | |
2399 for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ ) | |
2400 { | |
2401 nTh = oSettings.aoColumns[i].nTh; | |
2402 nTh.innerHTML = oSettings.aoColumns[i].sTitle; | |
2403 | |
2404 if ( oSettings.aoColumns[i].bVisible ) | |
2405 { | |
2406 if ( oSettings.aoColumns[i].sClass !== null ) | |
2407 { | |
2408 nTh.className = oSettings.aoColumns[i].sClass; | |
2409 } | |
2410 | |
2411 if ( oSettings.aoColumns[i].sWidth !== null ) | |
2412 { | |
2413 nTh.style.width = oSettings.aoColumns[i].sWidth; | |
2414 } | |
2415 | |
2416 nTr.appendChild( nTh ); | |
2417 } | |
2418 } | |
2419 $('thead:eq(0)', oSettings.nTable).html( '' )[0].appendChild( nTr ); | |
2420 } | |
2421 | |
2422 /* Add the extra markup needed by jQuery UI's themes */ | |
2423 if ( oSettings.bJUI ) | |
2424 { | |
2425 for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ ) | |
2426 { | |
2427 oSettings.aoColumns[i].nTh.insertBefore( document.createElement('span'), | |
2428 oSettings.aoColumns[i].nTh.firstChild ); | |
2429 } | |
2430 } | |
2431 | |
2432 /* Add sort listener */ | |
2433 if ( oSettings.oFeatures.bSort ) | |
2434 { | |
2435 for ( i=0 ; i<oSettings.aoColumns.length ; i++ ) | |
2436 { | |
2437 if ( oSettings.aoColumns[i].bSortable !== false ) | |
2438 { | |
2439 _fnSortAttachListener( oSettings, oSettings.aoColumns[i].nTh, i ); | |
2440 } | |
2441 else | |
2442 { | |
2443 $(oSettings.aoColumns[i].nTh).addClass( oSettings.oClasses.sSortableNone ); | |
2444 } | |
2445 } | |
2446 | |
2447 /* Take the brutal approach to cancelling text selection due to the shift key */ | |
2448 $('thead:eq(0) th', oSettings.nTable).mousedown( function (e) { | |
2449 if ( e.shiftKey ) | |
2450 { | |
2451 this.onselectstart = function() { return false; }; | |
2452 return false; | |
2453 } | |
2454 } ); | |
2455 } | |
2456 | |
2457 /* Cache the footer elements */ | |
2458 var nTfoot = oSettings.nTable.getElementsByTagName('tfoot'); | |
2459 if ( nTfoot.length !== 0 ) | |
2460 { | |
2461 iCorrector = 0; | |
2462 var nTfs = nTfoot[0].getElementsByTagName('th'); | |
2463 for ( i=0, iLen=nTfs.length ; i<iLen ; i++ ) | |
2464 { | |
2465 oSettings.aoColumns[i].nTf = nTfs[i-iCorrector]; | |
2466 if ( !oSettings.aoColumns[i].bVisible ) | |
2467 { | |
2468 nTfs[i-iCorrector].parentNode.removeChild( nTfs[i-iCorrector] ); | |
2469 iCorrector++; | |
2470 } | |
2471 } | |
2472 } | |
2473 } | |
2474 | |
2475 /* | |
2476 * Function: _fnDraw | |
2477 * Purpose: Insert the required TR nodes into the table for display | |
2478 * Returns: - | |
2479 * Inputs: object:oSettings - dataTables settings object | |
2480 */ | |
2481 function _fnDraw( oSettings ) | |
2482 { | |
2483 var i, iLen; | |
2484 var anRows = []; | |
2485 var iRowCount = 0; | |
2486 var bRowError = false; | |
2487 var iStrips = oSettings.asStripClasses.length; | |
2488 var iOpenRows = oSettings.aoOpenRows.length; | |
2489 | |
2490 /* If we are dealing with Ajax - do it here */ | |
2491 if ( oSettings.oFeatures.bServerSide && | |
2492 !_fnAjaxUpdate( oSettings ) ) | |
2493 { | |
2494 return; | |
2495 } | |
2496 | |
2497 /* Check and see if we have an initial draw position from state saving */ | |
2498 if ( typeof oSettings.iInitDisplayStart != 'undefined' && oSettings.iInitDisplayStart != -1 ) | |
2499 { | |
2500 oSettings._iDisplayStart = (oSettings.iInitDisplayStart >= oSettings.fnRecordsDisplay()) ? | |
2501 0 : oSettings.iInitDisplayStart; | |
2502 oSettings.iInitDisplayStart = -1; | |
2503 _fnCalculateEnd( oSettings ); | |
2504 } | |
2505 | |
2506 if ( oSettings.aiDisplay.length !== 0 ) | |
2507 { | |
2508 var iStart = oSettings._iDisplayStart; | |
2509 var iEnd = oSettings._iDisplayEnd; | |
2510 | |
2511 if ( oSettings.oFeatures.bServerSide ) | |
2512 { | |
2513 iStart = 0; | |
2514 iEnd = oSettings.aoData.length; | |
2515 } | |
2516 | |
2517 for ( var j=iStart ; j<iEnd ; j++ ) | |
2518 { | |
2519 var aoData = oSettings.aoData[ oSettings.aiDisplay[j] ]; | |
2520 var nRow = aoData.nTr; | |
2521 | |
2522 /* Remove the old stripping classes and then add the new one */ | |
2523 if ( iStrips !== 0 ) | |
2524 { | |
2525 var sStrip = oSettings.asStripClasses[ iRowCount % iStrips ]; | |
2526 if ( aoData._sRowStripe != sStrip ) | |
2527 { | |
2528 $(nRow).removeClass( aoData._sRowStripe ).addClass( sStrip ); | |
2529 aoData._sRowStripe = sStrip; | |
2530 } | |
2531 } | |
2532 | |
2533 /* Custom row callback function - might want to manipule the row */ | |
2534 if ( typeof oSettings.fnRowCallback == "function" ) | |
2535 { | |
2536 nRow = oSettings.fnRowCallback( nRow, | |
2537 oSettings.aoData[ oSettings.aiDisplay[j] ]._aData, iRowCount, j ); | |
2538 if ( !nRow && !bRowError ) | |
2539 { | |
2540 alert( "DataTables warning: A node was not returned by fnRowCallback" ); | |
2541 bRowError = true; | |
2542 } | |
2543 } | |
2544 | |
2545 anRows.push( nRow ); | |
2546 iRowCount++; | |
2547 | |
2548 /* If there is an open row - and it is attached to this parent - attach it on redraw */ | |
2549 if ( iOpenRows !== 0 ) | |
2550 { | |
2551 for ( var k=0 ; k<iOpenRows ; k++ ) | |
2552 { | |
2553 if ( nRow == oSettings.aoOpenRows[k].nParent ) | |
2554 { | |
2555 anRows.push( oSettings.aoOpenRows[k].nTr ); | |
2556 } | |
2557 } | |
2558 } | |
2559 } | |
2560 } | |
2561 else | |
2562 { | |
2563 /* Table is empty - create a row with an empty message in it */ | |
2564 anRows[ 0 ] = document.createElement( 'tr' ); | |
2565 | |
2566 if ( typeof oSettings.asStripClasses[0] != 'undefined' ) | |
2567 { | |
2568 anRows[ 0 ].className = oSettings.asStripClasses[0]; | |
2569 } | |
2570 | |
2571 var nTd = document.createElement( 'td' ); | |
2572 nTd.setAttribute( 'valign', "top" ); | |
2573 nTd.colSpan = oSettings.aoColumns.length; | |
2574 nTd.className = oSettings.oClasses.sRowEmpty; | |
2575 nTd.innerHTML = oSettings.oLanguage.sZeroRecords; | |
2576 | |
2577 anRows[ iRowCount ].appendChild( nTd ); | |
2578 } | |
2579 | |
2580 /* Callback the header and footer custom funcation if there is one */ | |
2581 if ( typeof oSettings.fnHeaderCallback == 'function' ) | |
2582 { | |
2583 oSettings.fnHeaderCallback( $('thead:eq(0)>tr', oSettings.nTable)[0], | |
2584 _fnGetDataMaster( oSettings ), oSettings._iDisplayStart, oSettings.fnDisplayEnd(), | |
2585 oSettings.aiDisplay ); | |
2586 } | |
2587 | |
2588 if ( typeof oSettings.fnFooterCallback == 'function' ) | |
2589 { | |
2590 oSettings.fnFooterCallback( $('tfoot:eq(0)>tr', oSettings.nTable)[0], | |
2591 _fnGetDataMaster( oSettings ), oSettings._iDisplayStart, oSettings.fnDisplayEnd(), | |
2592 oSettings.aiDisplay ); | |
2593 } | |
2594 | |
2595 /* | |
2596 * Need to remove any old row from the display - note we can't just empty the tbody using | |
2597 * $().html('') since this will unbind the jQuery event handlers (even although the node | |
2598 * still exists!) - equally we can't use innerHTML, since IE throws an exception. | |
2599 */ | |
2600 var nBody = oSettings.nTable.getElementsByTagName('tbody'); | |
2601 if ( nBody[0] ) | |
2602 { | |
2603 var nTrs = nBody[0].childNodes; | |
2604 for ( i=nTrs.length-1 ; i>=0 ; i-- ) | |
2605 { | |
2606 nTrs[i].parentNode.removeChild( nTrs[i] ); | |
2607 } | |
2608 | |
2609 /* Put the draw table into the dom */ | |
2610 for ( i=0, iLen=anRows.length ; i<iLen ; i++ ) | |
2611 { | |
2612 nBody[0].appendChild( anRows[i] ); | |
2613 } | |
2614 } | |
2615 | |
2616 /* Call all required callback functions for the end of a draw */ | |
2617 for ( i=0, iLen=oSettings.aoDrawCallback.length ; i<iLen ; i++ ) | |
2618 { | |
2619 oSettings.aoDrawCallback[i].fn( oSettings ); | |
2620 } | |
2621 | |
2622 /* Draw is complete, sorting and filtering must be as well */ | |
2623 oSettings.bSorted = false; | |
2624 oSettings.bFiltered = false; | |
2625 | |
2626 /* Perform certain DOM operations after the table has been drawn for the first time */ | |
2627 if ( typeof oSettings._bInitComplete == "undefined" ) | |
2628 { | |
2629 oSettings._bInitComplete = true; | |
2630 | |
2631 /* Set an absolute width for the table such that pagination doesn't | |
2632 * cause the table to resize | |
2633 */ | |
2634 if ( oSettings.oFeatures.bAutoWidth && oSettings.nTable.offsetWidth !== 0 ) | |
2635 { | |
2636 oSettings.nTable.style.width = oSettings.nTable.offsetWidth+"px"; | |
2637 } | |
2638 } | |
2639 } | |
2640 | |
2641 /* | |
2642 * Function: _fnReDraw | |
2643 * Purpose: Redraw the table - taking account of the various features which are enabled | |
2644 * Returns: - | |
2645 * Inputs: object:oSettings - dataTables settings object | |
2646 */ | |
2647 function _fnReDraw( oSettings ) | |
2648 { | |
2649 if ( oSettings.oFeatures.bSort ) | |
2650 { | |
2651 /* Sorting will refilter and draw for us */ | |
2652 _fnSort( oSettings, oSettings.oPreviousSearch ); | |
2653 } | |
2654 else if ( oSettings.oFeatures.bFilter ) | |
2655 { | |
2656 /* Filtering will redraw for us */ | |
2657 _fnFilterComplete( oSettings, oSettings.oPreviousSearch ); | |
2658 } | |
2659 else | |
2660 { | |
2661 _fnCalculateEnd( oSettings ); | |
2662 _fnDraw( oSettings ); | |
2663 } | |
2664 } | |
2665 | |
2666 /* | |
2667 * Function: _fnAjaxUpdate | |
2668 * Purpose: Update the table using an Ajax call | |
2669 * Returns: bool: block the table drawing or not | |
2670 * Inputs: object:oSettings - dataTables settings object | |
2671 */ | |
2672 function _fnAjaxUpdate( oSettings ) | |
2673 { | |
2674 if ( oSettings.bAjaxDataGet ) | |
2675 { | |
2676 _fnProcessingDisplay( oSettings, true ); | |
2677 var iColumns = oSettings.aoColumns.length; | |
2678 var aoData = []; | |
2679 var i; | |
2680 | |
2681 /* Paging and general */ | |
2682 oSettings.iServerDraw++; | |
2683 aoData.push( { "name": "sEcho", "value": oSettings.iServerDraw } ); | |
2684 aoData.push( { "name": "iColumns", "value": iColumns } ); | |
2685 aoData.push( { "name": "sColumns", "value": _fnColumnOrdering(oSettings) } ); | |
2686 aoData.push( { "name": "iDisplayStart", "value": oSettings._iDisplayStart } ); | |
2687 aoData.push( { "name": "iDisplayLength", "value": oSettings.oFeatures.bPaginate !== false ? | |
2688 oSettings._iDisplayLength : -1 } ); | |
2689 | |
2690 /* Filtering */ | |
2691 if ( oSettings.oFeatures.bFilter !== false ) | |
2692 { | |
2693 aoData.push( { "name": "sSearch", "value": oSettings.oPreviousSearch.sSearch } ); | |
2694 aoData.push( { "name": "bEscapeRegex", "value": oSettings.oPreviousSearch.bEscapeRegex } ); | |
2695 for ( i=0 ; i<iColumns ; i++ ) | |
2696 { | |
2697 aoData.push( { "name": "sSearch_"+i, "value": oSettings.aoPreSearchCols[i].sSearch } ); | |
2698 aoData.push( { "name": "bEscapeRegex_"+i, "value": oSettings.aoPreSearchCols[i].bEscapeRegex } ); | |
2699 aoData.push( { "name": "bSearchable_"+i, "value": oSettings.aoColumns[i].bSearchable } ); | |
2700 } | |
2701 } | |
2702 | |
2703 /* Sorting */ | |
2704 if ( oSettings.oFeatures.bSort !== false ) | |
2705 { | |
2706 var iFixed = oSettings.aaSortingFixed !== null ? oSettings.aaSortingFixed.length : 0; | |
2707 var iUser = oSettings.aaSorting.length; | |
2708 aoData.push( { "name": "iSortingCols", "value": iFixed+iUser } ); | |
2709 for ( i=0 ; i<iFixed ; i++ ) | |
2710 { | |
2711 aoData.push( { "name": "iSortCol_"+i, "value": oSettings.aaSortingFixed[i][0] } ); | |
2712 aoData.push( { "name": "sSortDir_"+i, "value": oSettings.aaSortingFixed[i][1] } ); | |
2713 } | |
2714 | |
2715 for ( i=0 ; i<iUser ; i++ ) | |
2716 { | |
2717 aoData.push( { "name": "iSortCol_"+(i+iFixed), "value": oSettings.aaSorting[i][0] } ); | |
2718 aoData.push( { "name": "sSortDir_"+(i+iFixed), "value": oSettings.aaSorting[i][1] } ); | |
2719 } | |
2720 | |
2721 for ( i=0 ; i<iColumns ; i++ ) | |
2722 { | |
2723 aoData.push( { "name": "bSortable_"+i, "value": oSettings.aoColumns[i].bSortable } ); | |
2724 } | |
2725 } | |
2726 | |
2727 oSettings.fnServerData( oSettings.sAjaxSource, aoData, function(json) { | |
2728 _fnAjaxUpdateDraw( oSettings, json ); | |
2729 } ); | |
2730 return false; | |
2731 } | |
2732 else | |
2733 { | |
2734 return true; | |
2735 } | |
2736 } | |
2737 | |
2738 /* | |
2739 * Function: _fnAjaxUpdateDraw | |
2740 * Purpose: Data the data from the server (nuking the old) and redraw the table | |
2741 * Returns: - | |
2742 * Inputs: object:oSettings - dataTables settings object | |
2743 * object:json - json data return from the server. | |
2744 * The following must be defined: | |
2745 * iTotalRecords, iTotalDisplayRecords, aaData | |
2746 * The following may be defined: | |
2747 * sColumns | |
2748 */ | |
2749 function _fnAjaxUpdateDraw ( oSettings, json ) | |
2750 { | |
2751 if ( typeof json.sEcho != 'undefined' ) | |
2752 { | |
2753 /* Protect against old returns over-writing a new one. Possible when you get | |
2754 * very fast interaction, and later queires are completed much faster | |
2755 */ | |
2756 if ( json.sEcho*1 < oSettings.iServerDraw ) | |
2757 { | |
2758 return; | |
2759 } | |
2760 else | |
2761 { | |
2762 oSettings.iServerDraw = json.sEcho * 1; | |
2763 } | |
2764 } | |
2765 | |
2766 _fnClearTable( oSettings ); | |
2767 oSettings._iRecordsTotal = json.iTotalRecords; | |
2768 oSettings._iRecordsDisplay = json.iTotalDisplayRecords; | |
2769 | |
2770 /* Determine if reordering is required */ | |
2771 var sOrdering = _fnColumnOrdering(oSettings); | |
2772 var bReOrder = (typeof json.sColumns != 'undefined' && sOrdering !== "" && json.sColumns != sOrdering ); | |
2773 if ( bReOrder ) | |
2774 { | |
2775 var aiIndex = _fnReOrderIndex( oSettings, json.sColumns ); | |
2776 } | |
2777 | |
2778 for ( var i=0, iLen=json.aaData.length ; i<iLen ; i++ ) | |
2779 { | |
2780 if ( bReOrder ) | |
2781 { | |
2782 /* If we need to re-order, then create a new array with the correct order and add it */ | |
2783 var aData = []; | |
2784 for ( var j=0, jLen=oSettings.aoColumns.length ; j<jLen ; j++ ) | |
2785 { | |
2786 aData.push( json.aaData[i][ aiIndex[j] ] ); | |
2787 } | |
2788 _fnAddData( oSettings, aData ); | |
2789 } | |
2790 else | |
2791 { | |
2792 /* No re-order required, sever got it "right" - just straight add */ | |
2793 _fnAddData( oSettings, json.aaData[i] ); | |
2794 } | |
2795 } | |
2796 oSettings.aiDisplay = oSettings.aiDisplayMaster.slice(); | |
2797 | |
2798 oSettings.bAjaxDataGet = false; | |
2799 _fnDraw( oSettings ); | |
2800 oSettings.bAjaxDataGet = true; | |
2801 _fnProcessingDisplay( oSettings, false ); | |
2802 } | |
2803 | |
2804 | |
2805 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * | |
2806 * Section - Options (features) HTML | |
2807 */ | |
2808 | |
2809 /* | |
2810 * Function: _fnAddOptionsHtml | |
2811 * Purpose: Add the options to the page HTML for the table | |
2812 * Returns: - | |
2813 * Inputs: object:oSettings - dataTables settings object | |
2814 */ | |
2815 function _fnAddOptionsHtml ( oSettings ) | |
2816 { | |
2817 /* | |
2818 * Create a temporary, empty, div which we can later on replace with what we have generated | |
2819 * we do it this way to rendering the 'options' html offline - speed :-) | |
2820 */ | |
2821 var nHolding = document.createElement( 'div' ); | |
2822 oSettings.nTable.parentNode.insertBefore( nHolding, oSettings.nTable ); | |
2823 | |
2824 /* | |
2825 * All DataTables are wrapped in a div - this is not currently optional - backwards | |
2826 * compatability. It can be removed if you don't want it. | |
2827 */ | |
2828 var nWrapper = document.createElement( 'div' ); | |
2829 nWrapper.className = oSettings.oClasses.sWrapper; | |
2830 if ( oSettings.sTableId !== '' ) | |
2831 { | |
2832 nWrapper.setAttribute( 'id', oSettings.sTableId+'_wrapper' ); | |
2833 } | |
2834 | |
2835 /* Track where we want to insert the option */ | |
2836 var nInsertNode = nWrapper; | |
2837 | |
2838 /* Substitute any constants in the dom string */ | |
2839 var sDom = oSettings.sDom.replace( "H", "fg-toolbar ui-widget-header ui-corner-tl ui-corner-tr ui-helper-clearfix" ); | |
2840 sDom = sDom.replace( "F", "fg-toolbar ui-widget-header ui-corner-bl ui-corner-br ui-helper-clearfix" ); | |
2841 | |
2842 /* Loop over the user set positioning and place the elements as needed */ | |
2843 var aDom = sDom.split(''); | |
2844 var nTmp, iPushFeature, cOption, nNewNode, cNext, sClass, j; | |
2845 for ( var i=0 ; i<aDom.length ; i++ ) | |
2846 { | |
2847 iPushFeature = 0; | |
2848 cOption = aDom[i]; | |
2849 | |
2850 if ( cOption == '<' ) | |
2851 { | |
2852 /* New container div */ | |
2853 nNewNode = document.createElement( 'div' ); | |
2854 | |
2855 /* Check to see if we should append a class name to the container */ | |
2856 cNext = aDom[i+1]; | |
2857 if ( cNext == "'" || cNext == '"' ) | |
2858 { | |
2859 sClass = ""; | |
2860 j = 2; | |
2861 while ( aDom[i+j] != cNext ) | |
2862 { | |
2863 sClass += aDom[i+j]; | |
2864 j++; | |
2865 } | |
2866 nNewNode.className = sClass; | |
2867 i += j; /* Move along the position array */ | |
2868 } | |
2869 | |
2870 nInsertNode.appendChild( nNewNode ); | |
2871 nInsertNode = nNewNode; | |
2872 } | |
2873 else if ( cOption == '>' ) | |
2874 { | |
2875 /* End container div */ | |
2876 nInsertNode = nInsertNode.parentNode; | |
2877 } | |
2878 else if ( cOption == 'l' && oSettings.oFeatures.bPaginate && oSettings.oFeatures.bLengthChange ) | |
2879 { | |
2880 /* Length */ | |
2881 nTmp = _fnFeatureHtmlLength( oSettings ); | |
2882 iPushFeature = 1; | |
2883 } | |
2884 else if ( cOption == 'f' && oSettings.oFeatures.bFilter ) | |
2885 { | |
2886 /* Filter */ | |
2887 nTmp = _fnFeatureHtmlFilter( oSettings ); | |
2888 iPushFeature = 1; | |
2889 } | |
2890 else if ( cOption == 'r' && oSettings.oFeatures.bProcessing ) | |
2891 { | |
2892 /* pRocessing */ | |
2893 nTmp = _fnFeatureHtmlProcessing( oSettings ); | |
2894 iPushFeature = 1; | |
2895 } | |
2896 else if ( cOption == 't' ) | |
2897 { | |
2898 /* Table */ | |
2899 nTmp = oSettings.nTable; | |
2900 iPushFeature = 1; | |
2901 } | |
2902 else if ( cOption == 'i' && oSettings.oFeatures.bInfo ) | |
2903 { | |
2904 /* Info */ | |
2905 nTmp = _fnFeatureHtmlInfo( oSettings ); | |
2906 iPushFeature = 1; | |
2907 } | |
2908 else if ( cOption == 'p' && oSettings.oFeatures.bPaginate ) | |
2909 { | |
2910 /* Pagination */ | |
2911 nTmp = _fnFeatureHtmlPaginate( oSettings ); | |
2912 iPushFeature = 1; | |
2913 } | |
2914 else if ( _oExt.aoFeatures.length !== 0 ) | |
2915 { | |
2916 /* Plug-in features */ | |
2917 var aoFeatures = _oExt.aoFeatures; | |
2918 for ( var k=0, kLen=aoFeatures.length ; k<kLen ; k++ ) | |
2919 { | |
2920 if ( cOption == aoFeatures[k].cFeature ) | |
2921 { | |
2922 nTmp = aoFeatures[k].fnInit( oSettings ); | |
2923 if ( nTmp ) | |
2924 { | |
2925 iPushFeature = 1; | |
2926 } | |
2927 break; | |
2928 } | |
2929 } | |
2930 } | |
2931 | |
2932 /* Add to the 2D features array */ | |
2933 if ( iPushFeature == 1 ) | |
2934 { | |
2935 if ( typeof oSettings.aanFeatures[cOption] != 'object' ) | |
2936 { | |
2937 oSettings.aanFeatures[cOption] = []; | |
2938 } | |
2939 oSettings.aanFeatures[cOption].push( nTmp ); | |
2940 nInsertNode.appendChild( nTmp ); | |
2941 } | |
2942 } | |
2943 | |
2944 /* Built our DOM structure - replace the holding div with what we want */ | |
2945 nHolding.parentNode.replaceChild( nWrapper, nHolding ); | |
2946 } | |
2947 | |
2948 | |
2949 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * | |
2950 * Section - Feature: Filtering | |
2951 */ | |
2952 | |
2953 /* | |
2954 * Function: _fnFeatureHtmlFilter | |
2955 * Purpose: Generate the node required for filtering text | |
2956 * Returns: node | |
2957 * Inputs: object:oSettings - dataTables settings object | |
2958 */ | |
2959 function _fnFeatureHtmlFilter ( oSettings ) | |
2960 { | |
2961 var nFilter = document.createElement( 'div' ); | |
2962 if ( oSettings.sTableId !== '' && typeof oSettings.aanFeatures.f == "undefined" ) | |
2963 { | |
2964 nFilter.setAttribute( 'id', oSettings.sTableId+'_filter' ); | |
2965 } | |
2966 nFilter.className = oSettings.oClasses.sFilter; | |
2967 var sSpace = oSettings.oLanguage.sSearch==="" ? "" : " "; | |
2968 nFilter.innerHTML = oSettings.oLanguage.sSearch+sSpace+'<input type="text" />'; | |
2969 | |
2970 var jqFilter = $("input", nFilter); | |
2971 jqFilter.val( oSettings.oPreviousSearch.sSearch.replace('"','"') ); | |
2972 jqFilter.keyup( function(e) { | |
2973 /* Update all other filter input elements for the new display */ | |
2974 var n = oSettings.aanFeatures.f; | |
2975 for ( var i=0, iLen=n.length ; i<iLen ; i++ ) | |
2976 { | |
2977 if ( n[i] != this.parentNode ) | |
2978 { | |
2979 $('input', n[i]).val( this.value ); | |
2980 } | |
2981 } | |
2982 | |
2983 /* Now do the filter */ | |
2984 _fnFilterComplete( oSettings, { | |
2985 "sSearch": this.value, | |
2986 "bEscapeRegex": oSettings.oPreviousSearch.bEscapeRegex | |
2987 } ); | |
2988 } ); | |
2989 | |
2990 jqFilter.keypress( function(e) { | |
2991 /* Prevent default */ | |
2992 if ( e.keyCode == 13 ) | |
2993 { | |
2994 return false; | |
2995 } | |
2996 } ); | |
2997 | |
2998 return nFilter; | |
2999 } | |
3000 | |
3001 /* | |
3002 * Function: _fnFilterComplete | |
3003 * Purpose: Filter the table using both the global filter and column based filtering | |
3004 * Returns: - | |
3005 * Inputs: object:oSettings - dataTables settings object | |
3006 * object:oSearch: search information | |
3007 * int:iForce - optional - force a research of the master array (1) or not (undefined or 0) | |
3008 */ | |
3009 function _fnFilterComplete ( oSettings, oInput, iForce ) | |
3010 { | |
3011 /* Filter on everything */ | |
3012 _fnFilter( oSettings, oInput.sSearch, iForce, oInput.bEscapeRegex ); | |
3013 | |
3014 /* Now do the individual column filter */ | |
3015 for ( var i=0 ; i<oSettings.aoPreSearchCols.length ; i++ ) | |
3016 { | |
3017 _fnFilterColumn( oSettings, oSettings.aoPreSearchCols[i].sSearch, i, | |
3018 oSettings.aoPreSearchCols[i].bEscapeRegex ); | |
3019 } | |
3020 | |
3021 /* Custom filtering */ | |
3022 if ( _oExt.afnFiltering.length !== 0 ) | |
3023 { | |
3024 _fnFilterCustom( oSettings ); | |
3025 } | |
3026 | |
3027 /* Tell the draw function we have been filtering */ | |
3028 oSettings.bFiltered = true; | |
3029 | |
3030 /* Redraw the table */ | |
3031 oSettings._iDisplayStart = 0; | |
3032 _fnCalculateEnd( oSettings ); | |
3033 _fnDraw( oSettings ); | |
3034 | |
3035 /* Rebuild search array 'offline' */ | |
3036 _fnBuildSearchArray( oSettings, 0 ); | |
3037 } | |
3038 | |
3039 /* | |
3040 * Function: _fnFilterCustom | |
3041 * Purpose: Apply custom filtering functions | |
3042 * Returns: - | |
3043 * Inputs: object:oSettings - dataTables settings object | |
3044 */ | |
3045 function _fnFilterCustom( oSettings ) | |
3046 { | |
3047 var afnFilters = _oExt.afnFiltering; | |
3048 for ( var i=0, iLen=afnFilters.length ; i<iLen ; i++ ) | |
3049 { | |
3050 var iCorrector = 0; | |
3051 for ( var j=0, jLen=oSettings.aiDisplay.length ; j<jLen ; j++ ) | |
3052 { | |
3053 var iDisIndex = oSettings.aiDisplay[j-iCorrector]; | |
3054 | |
3055 /* Check if we should use this row based on the filtering function */ | |
3056 if ( !afnFilters[i]( oSettings, oSettings.aoData[iDisIndex]._aData, iDisIndex ) ) | |
3057 { | |
3058 oSettings.aiDisplay.splice( j-iCorrector, 1 ); | |
3059 iCorrector++; | |
3060 } | |
3061 } | |
3062 } | |
3063 } | |
3064 | |
3065 /* | |
3066 * Function: _fnFilterColumn | |
3067 * Purpose: Filter the table on a per-column basis | |
3068 * Returns: - | |
3069 * Inputs: object:oSettings - dataTables settings object | |
3070 * string:sInput - string to filter on | |
3071 * int:iColumn - column to filter | |
3072 * bool:bEscapeRegex - escape regex or not | |
3073 */ | |
3074 function _fnFilterColumn ( oSettings, sInput, iColumn, bEscapeRegex ) | |
3075 { | |
3076 if ( sInput === "" ) | |
3077 { | |
3078 return; | |
3079 } | |
3080 | |
3081 var iIndexCorrector = 0; | |
3082 var sRegexMatch = bEscapeRegex ? _fnEscapeRegex( sInput ) : sInput; | |
3083 var rpSearch = new RegExp( sRegexMatch, "i" ); | |
3084 | |
3085 for ( var i=oSettings.aiDisplay.length-1 ; i>=0 ; i-- ) | |
3086 { | |
3087 var sData = _fnDataToSearch( oSettings.aoData[ oSettings.aiDisplay[i] ]._aData[iColumn], | |
3088 oSettings.aoColumns[iColumn].sType ); | |
3089 if ( ! rpSearch.test( sData ) ) | |
3090 { | |
3091 oSettings.aiDisplay.splice( i, 1 ); | |
3092 iIndexCorrector++; | |
3093 } | |
3094 } | |
3095 } | |
3096 | |
3097 /* | |
3098 * Function: _fnFilter | |
3099 * Purpose: Filter the data table based on user input and draw the table | |
3100 * Returns: - | |
3101 * Inputs: object:oSettings - dataTables settings object | |
3102 * string:sInput - string to filter on | |
3103 * int:iForce - optional - force a research of the master array (1) or not (undefined or 0) | |
3104 * bool:bEscapeRegex - escape regex or not | |
3105 */ | |
3106 function _fnFilter( oSettings, sInput, iForce, bEscapeRegex ) | |
3107 { | |
3108 var i; | |
3109 | |
3110 /* Check if we are forcing or not - optional parameter */ | |
3111 if ( typeof iForce == 'undefined' || iForce === null ) | |
3112 { | |
3113 iForce = 0; | |
3114 } | |
3115 | |
3116 /* Need to take account of custom filtering functions always */ | |
3117 if ( _oExt.afnFiltering.length !== 0 ) | |
3118 { | |
3119 iForce = 1; | |
3120 } | |
3121 | |
3122 /* Generate the regular expression to use. Something along the lines of: | |
3123 * ^(?=.*?\bone\b)(?=.*?\btwo\b)(?=.*?\bthree\b).*$ | |
3124 */ | |
3125 var asSearch = bEscapeRegex ? | |
3126 _fnEscapeRegex( sInput ).split( ' ' ) : | |
3127 sInput.split( ' ' ); | |
3128 var sRegExpString = '^(?=.*?'+asSearch.join( ')(?=.*?' )+').*$'; | |
3129 var rpSearch = new RegExp( sRegExpString, "i" ); /* case insensitive */ | |
3130 | |
3131 /* | |
3132 * If the input is blank - we want the full data set | |
3133 */ | |
3134 if ( sInput.length <= 0 ) | |
3135 { | |
3136 oSettings.aiDisplay.splice( 0, oSettings.aiDisplay.length); | |
3137 oSettings.aiDisplay = oSettings.aiDisplayMaster.slice(); | |
3138 } | |
3139 else | |
3140 { | |
3141 /* | |
3142 * We are starting a new search or the new search string is smaller | |
3143 * then the old one (i.e. delete). Search from the master array | |
3144 */ | |
3145 if ( oSettings.aiDisplay.length == oSettings.aiDisplayMaster.length || | |
3146 oSettings.oPreviousSearch.sSearch.length > sInput.length || iForce == 1 || | |
3147 sInput.indexOf(oSettings.oPreviousSearch.sSearch) !== 0 ) | |
3148 { | |
3149 /* Nuke the old display array - we are going to rebuild it */ | |
3150 oSettings.aiDisplay.splice( 0, oSettings.aiDisplay.length); | |
3151 | |
3152 /* Force a rebuild of the search array */ | |
3153 _fnBuildSearchArray( oSettings, 1 ); | |
3154 | |
3155 /* Search through all records to populate the search array | |
3156 * The the oSettings.aiDisplayMaster and asDataSearch arrays have 1 to 1 | |
3157 * mapping | |
3158 */ | |
3159 for ( i=0 ; i<oSettings.aiDisplayMaster.length ; i++ ) | |
3160 { | |
3161 if ( rpSearch.test(oSettings.asDataSearch[i]) ) | |
3162 { | |
3163 oSettings.aiDisplay.push( oSettings.aiDisplayMaster[i] ); | |
3164 } | |
3165 } | |
3166 } | |
3167 else | |
3168 { | |
3169 /* Using old search array - refine it - do it this way for speed | |
3170 * Don't have to search the whole master array again | |
3171 */ | |
3172 var iIndexCorrector = 0; | |
3173 | |
3174 /* Search the current results */ | |
3175 for ( i=0 ; i<oSettings.asDataSearch.length ; i++ ) | |
3176 { | |
3177 if ( ! rpSearch.test(oSettings.asDataSearch[i]) ) | |
3178 { | |
3179 oSettings.aiDisplay.splice( i-iIndexCorrector, 1 ); | |
3180 iIndexCorrector++; | |
3181 } | |
3182 } | |
3183 } | |
3184 } | |
3185 oSettings.oPreviousSearch.sSearch = sInput; | |
3186 oSettings.oPreviousSearch.bEscapeRegex = bEscapeRegex; | |
3187 } | |
3188 | |
3189 /* | |
3190 * Function: _fnBuildSearchArray | |
3191 * Purpose: Create an array which can be quickly search through | |
3192 * Returns: - | |
3193 * Inputs: object:oSettings - dataTables settings object | |
3194 * int:iMaster - use the master data array - optional | |
3195 */ | |
3196 function _fnBuildSearchArray ( oSettings, iMaster ) | |
3197 { | |
3198 /* Clear out the old data */ | |
3199 oSettings.asDataSearch.splice( 0, oSettings.asDataSearch.length ); | |
3200 | |
3201 var aArray = (typeof iMaster != 'undefined' && iMaster == 1) ? | |
3202 oSettings.aiDisplayMaster : oSettings.aiDisplay; | |
3203 | |
3204 for ( var i=0, iLen=aArray.length ; i<iLen ; i++ ) | |
3205 { | |
3206 oSettings.asDataSearch[i] = ''; | |
3207 for ( var j=0, jLen=oSettings.aoColumns.length ; j<jLen ; j++ ) | |
3208 { | |
3209 if ( oSettings.aoColumns[j].bSearchable ) | |
3210 { | |
3211 var sData = oSettings.aoData[ aArray[i] ]._aData[j]; | |
3212 oSettings.asDataSearch[i] += _fnDataToSearch( sData, oSettings.aoColumns[j].sType )+' '; | |
3213 } | |
3214 } | |
3215 } | |
3216 } | |
3217 | |
3218 /* | |
3219 * Function: _fnDataToSearch | |
3220 * Purpose: Convert raw data into something that the user can search on | |
3221 * Returns: string: - search string | |
3222 * Inputs: string:sData - data to be modified | |
3223 * string:sType - data type | |
3224 */ | |
3225 function _fnDataToSearch ( sData, sType ) | |
3226 { | |
3227 | |
3228 if ( typeof _oExt.ofnSearch[sType] == "function" ) | |
3229 { | |
3230 return _oExt.ofnSearch[sType]( sData ); | |
3231 } | |
3232 else if ( sType == "html" ) | |
3233 { | |
3234 return sData.replace(/\n/g," ").replace( /<.*?>/g, "" ); | |
3235 } | |
3236 else if ( typeof sData == "string" ) | |
3237 { | |
3238 return sData.replace(/\n/g," "); | |
3239 } | |
3240 return sData; | |
3241 } | |
3242 | |
3243 | |
3244 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * | |
3245 * Section - Feature: Sorting | |
3246 */ | |
3247 | |
3248 /* | |
3249 * Function: _fnSort | |
3250 * Purpose: Change the order of the table | |
3251 * Returns: - | |
3252 * Inputs: object:oSettings - dataTables settings object | |
3253 * bool:bApplyClasses - optional - should we apply classes or not | |
3254 * Notes: We always sort the master array and then apply a filter again | |
3255 * if it is needed. This probably isn't optimal - but atm I can't think | |
3256 * of any other way which is (each has disadvantages). we want to sort aiDisplayMaster - | |
3257 * but according to aoData[]._aData | |
3258 */ | |
3259 function _fnSort ( oSettings, bApplyClasses ) | |
3260 { | |
3261 var aaSort = []; | |
3262 var oSort = _oExt.oSort; | |
3263 var aoData = oSettings.aoData; | |
3264 var iDataSort; | |
3265 var iDataType; | |
3266 var i, j, jLen; | |
3267 | |
3268 /* No sorting required if server-side or no sorting array */ | |
3269 if ( !oSettings.oFeatures.bServerSide && | |
3270 (oSettings.aaSorting.length !== 0 || oSettings.aaSortingFixed !== null) ) | |
3271 { | |
3272 if ( oSettings.aaSortingFixed !== null ) | |
3273 { | |
3274 aaSort = oSettings.aaSortingFixed.concat( oSettings.aaSorting ); | |
3275 } | |
3276 else | |
3277 { | |
3278 aaSort = oSettings.aaSorting.slice(); | |
3279 } | |
3280 | |
3281 /* If there is a sorting data type, and a fuction belonging to it, then we need to | |
3282 * get the data from the developer's function and apply it for this column | |
3283 */ | |
3284 for ( i=0 ; i<aaSort.length ; i++ ) | |
3285 { | |
3286 var iColumn = aaSort[i][0]; | |
3287 var sDataType = oSettings.aoColumns[ iColumn ].sSortDataType; | |
3288 if ( typeof _oExt.afnSortData[sDataType] != 'undefined' ) | |
3289 { | |
3290 var iCorrector = 0; | |
3291 var aData = _oExt.afnSortData[sDataType]( oSettings, iColumn ); | |
3292 for ( j=0, jLen=aoData.length ; j<jLen ; j++ ) | |
3293 { | |
3294 if ( aoData[j] !== null ) | |
3295 { | |
3296 aoData[j]._aData[iColumn] = aData[iCorrector]; | |
3297 iCorrector++; | |
3298 } | |
3299 } | |
3300 } | |
3301 } | |
3302 | |
3303 /* DataTables offers two different methods for doing the 2D array sorting over multiple | |
3304 * columns. The first is to construct a function dynamically, and then evaluate and run | |
3305 * the function, while the second has no need for evalulation, but is a little bit slower. | |
3306 * This is used for environments which do not allow eval() for code execuation such as AIR | |
3307 */ | |
3308 if ( !window.runtime ) | |
3309 { | |
3310 /* Dynamically created sorting function. Based on the information that we have, we can | |
3311 * create a sorting function as if it were specifically written for this sort. Here we | |
3312 * want to build a function something like (for two column sorting): | |
3313 * fnLocalSorting = function(a,b){ | |
3314 * var iTest; | |
3315 * iTest = oSort['string-asc']('data11', 'data12'); | |
3316 * if (iTest === 0) | |
3317 * iTest = oSort['numeric-desc']('data21', 'data22'); | |
3318 * if (iTest === 0) | |
3319 * return oSort['numeric-desc'](1,2); | |
3320 * return iTest; | |
3321 * } | |
3322 * So basically we have a test for each column, and if that column matches, test the | |
3323 * next one. If all columns match, then we use a numeric sort on the position the two | |
3324 * row have in the original data array in order to provide a stable sort. | |
3325 */ | |
3326 var fnLocalSorting; | |
3327 var sDynamicSort = "fnLocalSorting = function(a,b){"+ | |
3328 "var iTest;"; | |
3329 | |
3330 for ( i=0 ; i<aaSort.length-1 ; i++ ) | |
3331 { | |
3332 iDataSort = oSettings.aoColumns[ aaSort[i][0] ].iDataSort; | |
3333 iDataType = oSettings.aoColumns[ iDataSort ].sType; | |
3334 sDynamicSort += "iTest = oSort['"+iDataType+"-"+aaSort[i][1]+"']"+ | |
3335 "( aoData[a]._aData["+iDataSort+"], aoData[b]._aData["+iDataSort+"] ); if ( iTest === 0 )"; | |
3336 } | |
3337 | |
3338 iDataSort = oSettings.aoColumns[ aaSort[aaSort.length-1][0] ].iDataSort; | |
3339 iDataType = oSettings.aoColumns[ iDataSort ].sType; | |
3340 sDynamicSort += "iTest = oSort['"+iDataType+"-"+aaSort[aaSort.length-1][1]+"']"+ | |
3341 "( aoData[a]._aData["+iDataSort+"], aoData[b]._aData["+iDataSort+"] );"+ | |
3342 "if (iTest===0) return oSort['numeric-"+aaSort[aaSort.length-1][1]+"'](a, b); "+ | |
3343 "return iTest;}"; | |
3344 | |
3345 /* The eval has to be done to a variable for IE */ | |
3346 eval( sDynamicSort ); | |
3347 oSettings.aiDisplayMaster.sort( fnLocalSorting ); | |
3348 } | |
3349 else | |
3350 { | |
3351 /* | |
3352 * Non-eval() sorting (AIR and other environments which doesn't allow code in eval() | |
3353 * Note that for reasonable sized data sets this method is around 1.5 times slower than | |
3354 * the eval above (hence why it is not used all the time). Oddly enough, it is ever so | |
3355 * slightly faster for very small sets (presumably the eval has overhead). | |
3356 * Single column (1083 records) - eval: 32mS AIR: 38mS | |
3357 * Two columns (1083 records) - eval: 55mS AIR: 66mS | |
3358 */ | |
3359 | |
3360 /* Build a cached array so the sort doesn't have to process this stuff on every call */ | |
3361 var aAirSort = []; | |
3362 var iLen = aaSort.length; | |
3363 for ( i=0 ; i<iLen ; i++ ) | |
3364 { | |
3365 iDataSort = oSettings.aoColumns[ aaSort[i][0] ].iDataSort; | |
3366 aAirSort.push( [ | |
3367 iDataSort, | |
3368 oSettings.aoColumns[ iDataSort ].sType+'-'+aaSort[i][1] | |
3369 ] ); | |
3370 } | |
3371 | |
3372 oSettings.aiDisplayMaster.sort( function (a,b) { | |
3373 var iTest; | |
3374 for ( var i=0 ; i<iLen ; i++ ) | |
3375 { | |
3376 iTest = oSort[ aAirSort[i][1] ]( aoData[a]._aData[aAirSort[i][0]], aoData[b]._aData[aAirSort[i][0]] ); | |
3377 if ( iTest !== 0 ) | |
3378 { | |
3379 return iTest; | |
3380 } | |
3381 } | |
3382 return 0; | |
3383 } ); | |
3384 } | |
3385 } | |
3386 | |
3387 /* Alter the sorting classes to take account of the changes */ | |
3388 if ( typeof bApplyClasses == 'undefined' || bApplyClasses ) | |
3389 { | |
3390 _fnSortingClasses( oSettings ); | |
3391 } | |
3392 | |
3393 /* Tell the draw function that we have sorted the data */ | |
3394 oSettings.bSorted = true; | |
3395 | |
3396 /* Copy the master data into the draw array and re-draw */ | |
3397 if ( oSettings.oFeatures.bFilter ) | |
3398 { | |
3399 /* _fnFilter() will redraw the table for us */ | |
3400 _fnFilterComplete( oSettings, oSettings.oPreviousSearch, 1 ); | |
3401 } | |
3402 else | |
3403 { | |
3404 oSettings.aiDisplay = oSettings.aiDisplayMaster.slice(); | |
3405 oSettings._iDisplayStart = 0; /* reset display back to page 0 */ | |
3406 _fnCalculateEnd( oSettings ); | |
3407 _fnDraw( oSettings ); | |
3408 } | |
3409 } | |
3410 | |
3411 /* | |
3412 * Function: _fnSortAttachListener | |
3413 * Purpose: Attach a sort handler (click) to a node | |
3414 * Returns: - | |
3415 * Inputs: object:oSettings - dataTables settings object | |
3416 * node:nNode - node to attach the handler to | |
3417 * int:iDataIndex - column sorting index | |
3418 * function:fnCallback - callback function - optional | |
3419 */ | |
3420 function _fnSortAttachListener ( oSettings, nNode, iDataIndex, fnCallback ) | |
3421 { | |
3422 $(nNode).click( function (e) { | |
3423 /* If the column is not sortable - don't to anything */ | |
3424 if ( oSettings.aoColumns[iDataIndex].bSortable === false ) | |
3425 { | |
3426 return; | |
3427 } | |
3428 | |
3429 /* | |
3430 * This is a little bit odd I admit... I declare a temporary function inside the scope of | |
3431 * _fnDrawHead and the click handler in order that the code presented here can be used | |
3432 * twice - once for when bProcessing is enabled, and another time for when it is | |
3433 * disabled, as we need to perform slightly different actions. | |
3434 * Basically the issue here is that the Javascript engine in modern browsers don't | |
3435 * appear to allow the rendering engine to update the display while it is still excuting | |
3436 * it's thread (well - it does but only after long intervals). This means that the | |
3437 * 'processing' display doesn't appear for a table sort. To break the js thread up a bit | |
3438 * I force an execution break by using setTimeout - but this breaks the expected | |
3439 * thread continuation for the end-developer's point of view (their code would execute | |
3440 * too early), so we on;y do it when we absolutely have to. | |
3441 */ | |
3442 var fnInnerSorting = function () { | |
3443 var iColumn, iNextSort; | |
3444 | |
3445 /* If the shift key is pressed then we are multipe column sorting */ | |
3446 if ( e.shiftKey ) | |
3447 { | |
3448 /* Are we already doing some kind of sort on this column? */ | |
3449 var bFound = false; | |
3450 for ( var i=0 ; i<oSettings.aaSorting.length ; i++ ) | |
3451 { | |
3452 if ( oSettings.aaSorting[i][0] == iDataIndex ) | |
3453 { | |
3454 bFound = true; | |
3455 iColumn = oSettings.aaSorting[i][0]; | |
3456 iNextSort = oSettings.aaSorting[i][2]+1; | |
3457 | |
3458 if ( typeof oSettings.aoColumns[iColumn].asSorting[iNextSort] == 'undefined' ) | |
3459 { | |
3460 /* Reached the end of the sorting options, remove from multi-col sort */ | |
3461 oSettings.aaSorting.splice( i, 1 ); | |
3462 } | |
3463 else | |
3464 { | |
3465 /* Move onto next sorting direction */ | |
3466 oSettings.aaSorting[i][1] = oSettings.aoColumns[iColumn].asSorting[iNextSort]; | |
3467 oSettings.aaSorting[i][2] = iNextSort; | |
3468 } | |
3469 break; | |
3470 } | |
3471 } | |
3472 | |
3473 /* No sort yet - add it in */ | |
3474 if ( bFound === false ) | |
3475 { | |
3476 oSettings.aaSorting.push( [ iDataIndex, | |
3477 oSettings.aoColumns[iDataIndex].asSorting[0], 0 ] ); | |
3478 } | |
3479 } | |
3480 else | |
3481 { | |
3482 /* If no shift key then single column sort */ | |
3483 if ( oSettings.aaSorting.length == 1 && oSettings.aaSorting[0][0] == iDataIndex ) | |
3484 { | |
3485 iColumn = oSettings.aaSorting[0][0]; | |
3486 iNextSort = oSettings.aaSorting[0][2]+1; | |
3487 if ( typeof oSettings.aoColumns[iColumn].asSorting[iNextSort] == 'undefined' ) | |
3488 { | |
3489 iNextSort = 0; | |
3490 } | |
3491 oSettings.aaSorting[0][1] = oSettings.aoColumns[iColumn].asSorting[iNextSort]; | |
3492 oSettings.aaSorting[0][2] = iNextSort; | |
3493 } | |
3494 else | |
3495 { | |
3496 oSettings.aaSorting.splice( 0, oSettings.aaSorting.length ); | |
3497 oSettings.aaSorting.push( [ iDataIndex, | |
3498 oSettings.aoColumns[iDataIndex].asSorting[0], 0 ] ); | |
3499 } | |
3500 } | |
3501 | |
3502 /* Run the sort */ | |
3503 _fnSort( oSettings ); | |
3504 }; /* /fnInnerSorting */ | |
3505 | |
3506 if ( !oSettings.oFeatures.bProcessing ) | |
3507 { | |
3508 fnInnerSorting(); | |
3509 } | |
3510 else | |
3511 { | |
3512 _fnProcessingDisplay( oSettings, true ); | |
3513 setTimeout( function() { | |
3514 fnInnerSorting(); | |
3515 if ( !oSettings.oFeatures.bServerSide ) | |
3516 { | |
3517 _fnProcessingDisplay( oSettings, false ); | |
3518 } | |
3519 }, 0 ); | |
3520 } | |
3521 | |
3522 /* Call the user specified callback function - used for async user interaction */ | |
3523 if ( typeof fnCallback == 'function' ) | |
3524 { | |
3525 fnCallback( oSettings ); | |
3526 } | |
3527 } ); | |
3528 } | |
3529 | |
3530 /* | |
3531 * Function: _fnSortingClasses | |
3532 * Purpose: Set the sortting classes on the header | |
3533 * Returns: - | |
3534 * Inputs: object:oSettings - dataTables settings object | |
3535 * Notes: It is safe to call this function when bSort is false | |
3536 */ | |
3537 function _fnSortingClasses( oSettings ) | |
3538 { | |
3539 var i, iLen, j, jLen, iFound; | |
3540 var aaSort, sClass; | |
3541 var iColumns = oSettings.aoColumns.length; | |
3542 var oClasses = oSettings.oClasses; | |
3543 | |
3544 for ( i=0 ; i<iColumns ; i++ ) | |
3545 { | |
3546 if ( oSettings.aoColumns[i].bSortable ) | |
3547 { | |
3548 $(oSettings.aoColumns[i].nTh).removeClass( oClasses.sSortAsc +" "+ oClasses.sSortDesc + | |
3549 " "+ oSettings.aoColumns[i].sSortingClass ); | |
3550 } | |
3551 } | |
3552 | |
3553 if ( oSettings.aaSortingFixed !== null ) | |
3554 { | |
3555 aaSort = oSettings.aaSortingFixed.concat( oSettings.aaSorting ); | |
3556 } | |
3557 else | |
3558 { | |
3559 aaSort = oSettings.aaSorting.slice(); | |
3560 } | |
3561 | |
3562 /* Apply the required classes to the header */ | |
3563 for ( i=0 ; i<oSettings.aoColumns.length ; i++ ) | |
3564 { | |
3565 if ( oSettings.aoColumns[i].bSortable ) | |
3566 { | |
3567 sClass = oSettings.aoColumns[i].sSortingClass; | |
3568 iFound = -1; | |
3569 for ( j=0 ; j<aaSort.length ; j++ ) | |
3570 { | |
3571 if ( aaSort[j][0] == i ) | |
3572 { | |
3573 sClass = ( aaSort[j][1] == "asc" ) ? | |
3574 oClasses.sSortAsc : oClasses.sSortDesc; | |
3575 iFound = j; | |
3576 break; | |
3577 } | |
3578 } | |
3579 $(oSettings.aoColumns[i].nTh).addClass( sClass ); | |
3580 | |
3581 if ( oSettings.bJUI ) | |
3582 { | |
3583 /* jQuery UI uses extra markup */ | |
3584 var jqSpan = $("span", oSettings.aoColumns[i].nTh); | |
3585 jqSpan.removeClass(oClasses.sSortJUIAsc +" "+ oClasses.sSortJUIDesc +" "+ | |
3586 oClasses.sSortJUI +" "+ oClasses.sSortJUIAscAllowed +" "+ oClasses.sSortJUIDescAllowed ); | |
3587 | |
3588 var sSpanClass; | |
3589 if ( iFound == -1 ) | |
3590 { | |
3591 sSpanClass = oSettings.aoColumns[i].sSortingClassJUI; | |
3592 } | |
3593 else if ( aaSort[iFound][1] == "asc" ) | |
3594 { | |
3595 sSpanClass = oClasses.sSortJUIAsc; | |
3596 } | |
3597 else | |
3598 { | |
3599 sSpanClass = oClasses.sSortJUIDesc; | |
3600 } | |
3601 | |
3602 jqSpan.addClass( sSpanClass ); | |
3603 } | |
3604 } | |
3605 else | |
3606 { | |
3607 /* No sorting on this column, so add the base class. This will have been assigned by | |
3608 * _fnAddColumn | |
3609 */ | |
3610 $(oSettings.aoColumns[i].nTh).addClass( oSettings.aoColumns[i].sSortingClass ); | |
3611 } | |
3612 } | |
3613 | |
3614 /* | |
3615 * Apply the required classes to the table body | |
3616 * Note that this is given as a feature switch since it can significantly slow down a sort | |
3617 * on large data sets (adding and removing of classes is always slow at the best of times..) | |
3618 * Further to this, note that this code is admitadly fairly ugly. It could be made a lot | |
3619 * simpiler using jQuery selectors and add/removeClass, but that is significantly slower | |
3620 * (on the order of 5 times slower) - hence the direct DOM manipulation here. | |
3621 */ | |
3622 sClass = oClasses.sSortColumn; | |
3623 | |
3624 if ( oSettings.oFeatures.bSort && oSettings.oFeatures.bSortClasses ) | |
3625 { | |
3626 var nTds = _fnGetTdNodes( oSettings ); | |
3627 | |
3628 /* Remove the old classes */ | |
3629 if ( nTds.length >= iColumns ) | |
3630 { | |
3631 for ( i=0 ; i<iColumns ; i++ ) | |
3632 { | |
3633 if ( nTds[i].className.indexOf(sClass+"1") != -1 ) | |
3634 { | |
3635 for ( j=0, jLen=(nTds.length/iColumns) ; j<jLen ; j++ ) | |
3636 { | |
3637 nTds[(iColumns*j)+i].className = | |
3638 nTds[(iColumns*j)+i].className.replace( " "+sClass+"1", "" ); | |
3639 } | |
3640 } | |
3641 else if ( nTds[i].className.indexOf(sClass+"2") != -1 ) | |
3642 { | |
3643 for ( j=0, jLen=(nTds.length/iColumns) ; j<jLen ; j++ ) | |
3644 { | |
3645 nTds[(iColumns*j)+i].className = | |
3646 nTds[(iColumns*j)+i].className.replace( " "+sClass+"2", "" ); | |
3647 } | |
3648 } | |
3649 else if ( nTds[i].className.indexOf(sClass+"3") != -1 ) | |
3650 { | |
3651 for ( j=0, jLen=(nTds.length/iColumns) ; j<jLen ; j++ ) | |
3652 { | |
3653 nTds[(iColumns*j)+i].className = | |
3654 nTds[(iColumns*j)+i].className.replace( " "+sClass+"3", "" ); | |
3655 } | |
3656 } | |
3657 } | |
3658 } | |
3659 | |
3660 /* Add the new classes to the table */ | |
3661 var iClass = 1, iTargetCol; | |
3662 for ( i=0 ; i<aaSort.length ; i++ ) | |
3663 { | |
3664 iTargetCol = parseInt( aaSort[i][0], 10 ); | |
3665 for ( j=0, jLen=(nTds.length/iColumns) ; j<jLen ; j++ ) | |
3666 { | |
3667 nTds[(iColumns*j)+iTargetCol].className += " "+sClass+iClass; | |
3668 } | |
3669 | |
3670 if ( iClass < 3 ) | |
3671 { | |
3672 iClass++; | |
3673 } | |
3674 } | |
3675 } | |
3676 } | |
3677 | |
3678 | |
3679 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * | |
3680 * Section - Feature: Pagination. Note that most of the paging logic is done in | |
3681 * _oExt.oPagination | |
3682 */ | |
3683 | |
3684 /* | |
3685 * Function: _fnFeatureHtmlPaginate | |
3686 * Purpose: Generate the node required for default pagination | |
3687 * Returns: node | |
3688 * Inputs: object:oSettings - dataTables settings object | |
3689 */ | |
3690 function _fnFeatureHtmlPaginate ( oSettings ) | |
3691 { | |
3692 var nPaginate = document.createElement( 'div' ); | |
3693 nPaginate.className = oSettings.oClasses.sPaging+oSettings.sPaginationType; | |
3694 | |
3695 _oExt.oPagination[ oSettings.sPaginationType ].fnInit( oSettings, nPaginate, | |
3696 function( oSettings ) { | |
3697 _fnCalculateEnd( oSettings ); | |
3698 _fnDraw( oSettings ); | |
3699 } | |
3700 ); | |
3701 | |
3702 /* Add a draw callback for the pagination on first instance, to update the paging display */ | |
3703 if ( typeof oSettings.aanFeatures.p == "undefined" ) | |
3704 { | |
3705 oSettings.aoDrawCallback.push( { | |
3706 "fn": function( oSettings ) { | |
3707 _oExt.oPagination[ oSettings.sPaginationType ].fnUpdate( oSettings, function( oSettings ) { | |
3708 _fnCalculateEnd( oSettings ); | |
3709 _fnDraw( oSettings ); | |
3710 } ); | |
3711 }, | |
3712 "sName": "pagination" | |
3713 } ); | |
3714 } | |
3715 return nPaginate; | |
3716 } | |
3717 | |
3718 /* | |
3719 * Function: _fnPageChange | |
3720 * Purpose: Alter the display settings to change the page | |
3721 * Returns: bool:true - page has changed, false - no change (no effect) eg 'first' on page 1 | |
3722 * Inputs: object:oSettings - dataTables settings object | |
3723 * string:sAction - paging action to take: "first", "previous", "next" or "last" | |
3724 */ | |
3725 function _fnPageChange ( oSettings, sAction ) | |
3726 { | |
3727 var iOldStart = oSettings._iDisplayStart; | |
3728 | |
3729 if ( sAction == "first" ) | |
3730 { | |
3731 oSettings._iDisplayStart = 0; | |
3732 } | |
3733 else if ( sAction == "previous" ) | |
3734 { | |
3735 oSettings._iDisplayStart = oSettings._iDisplayLength>=0 ? | |
3736 oSettings._iDisplayStart - oSettings._iDisplayLength : | |
3737 0; | |
3738 | |
3739 /* Correct for underrun */ | |
3740 if ( oSettings._iDisplayStart < 0 ) | |
3741 { | |
3742 oSettings._iDisplayStart = 0; | |
3743 } | |
3744 } | |
3745 else if ( sAction == "next" ) | |
3746 { | |
3747 if ( oSettings._iDisplayLength >= 0 ) | |
3748 { | |
3749 /* Make sure we are not over running the display array */ | |
3750 if ( oSettings._iDisplayStart + oSettings._iDisplayLength < oSettings.fnRecordsDisplay() ) | |
3751 { | |
3752 oSettings._iDisplayStart += oSettings._iDisplayLength; | |
3753 } | |
3754 } | |
3755 else | |
3756 { | |
3757 oSettings._iDisplayStart = 0; | |
3758 } | |
3759 } | |
3760 else if ( sAction == "last" ) | |
3761 { | |
3762 if ( oSettings._iDisplayLength >= 0 ) | |
3763 { | |
3764 var iPages = parseInt( (oSettings.fnRecordsDisplay()-1) / oSettings._iDisplayLength, 10 ) + 1; | |
3765 oSettings._iDisplayStart = (iPages-1) * oSettings._iDisplayLength; | |
3766 } | |
3767 else | |
3768 { | |
3769 oSettings._iDisplayStart = 0; | |
3770 } | |
3771 } | |
3772 else | |
3773 { | |
3774 alert( "DataTables warning: unknown paging action: "+sAction ); | |
3775 } | |
3776 | |
3777 return iOldStart != oSettings._iDisplayStart; | |
3778 } | |
3779 | |
3780 | |
3781 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * | |
3782 * Section - Feature: HTML info | |
3783 */ | |
3784 | |
3785 /* | |
3786 * Function: _fnFeatureHtmlInfo | |
3787 * Purpose: Generate the node required for the info display | |
3788 * Returns: node | |
3789 * Inputs: object:oSettings - dataTables settings object | |
3790 */ | |
3791 function _fnFeatureHtmlInfo ( oSettings ) | |
3792 { | |
3793 var nInfo = document.createElement( 'div' ); | |
3794 nInfo.className = oSettings.oClasses.sInfo; | |
3795 | |
3796 /* Actions that are to be taken once only for this feature */ | |
3797 if ( typeof oSettings.aanFeatures.i == "undefined" ) | |
3798 { | |
3799 /* Add draw callback */ | |
3800 oSettings.aoDrawCallback.push( { | |
3801 "fn": _fnUpdateInfo, | |
3802 "sName": "information" | |
3803 } ); | |
3804 | |
3805 /* Add id */ | |
3806 if ( oSettings.sTableId !== '' ) | |
3807 { | |
3808 nInfo.setAttribute( 'id', oSettings.sTableId+'_info' ); | |
3809 } | |
3810 } | |
3811 | |
3812 return nInfo; | |
3813 } | |
3814 | |
3815 /* | |
3816 * Function: _fnUpdateInfo | |
3817 * Purpose: Update the information elements in the display | |
3818 * Returns: - | |
3819 * Inputs: object:oSettings - dataTables settings object | |
3820 */ | |
3821 function _fnUpdateInfo ( oSettings ) | |
3822 { | |
3823 /* Show information about the table */ | |
3824 if ( !oSettings.oFeatures.bInfo || oSettings.aanFeatures.i.length === 0 ) | |
3825 { | |
3826 return; | |
3827 } | |
3828 | |
3829 var nFirst = oSettings.aanFeatures.i[0]; | |
3830 | |
3831 if ( oSettings.fnRecordsDisplay() === 0 && | |
3832 oSettings.fnRecordsDisplay() == oSettings.fnRecordsTotal() ) | |
3833 { | |
3834 /* Empty record set */ | |
3835 nFirst.innerHTML = oSettings.oLanguage.sInfoEmpty+ oSettings.oLanguage.sInfoPostFix; | |
3836 } | |
3837 else if ( oSettings.fnRecordsDisplay() === 0 ) | |
3838 { | |
3839 /* Rmpty record set after filtering */ | |
3840 nFirst.innerHTML = oSettings.oLanguage.sInfoEmpty +' '+ | |
3841 oSettings.oLanguage.sInfoFiltered.replace('_MAX_', | |
3842 oSettings.fnRecordsTotal())+ oSettings.oLanguage.sInfoPostFix; | |
3843 } | |
3844 else if ( oSettings.fnRecordsDisplay() == oSettings.fnRecordsTotal() ) | |
3845 { | |
3846 /* Normal record set */ | |
3847 nFirst.innerHTML = oSettings.oLanguage.sInfo. | |
3848 replace('_START_',oSettings._iDisplayStart+1). | |
3849 replace('_END_',oSettings.fnDisplayEnd()). | |
3850 replace('_TOTAL_',oSettings.fnRecordsDisplay())+ | |
3851 oSettings.oLanguage.sInfoPostFix; | |
3852 } | |
3853 else | |
3854 { | |
3855 /* Record set after filtering */ | |
3856 nFirst.innerHTML = | |
3857 oSettings.oLanguage.sInfo. | |
3858 replace('_START_',oSettings._iDisplayStart+1). | |
3859 replace('_END_',oSettings.fnDisplayEnd()). | |
3860 replace('_TOTAL_',oSettings.fnRecordsDisplay()) +' '+ | |
3861 oSettings.oLanguage.sInfoFiltered.replace('_MAX_', oSettings.fnRecordsTotal())+ | |
3862 oSettings.oLanguage.sInfoPostFix; | |
3863 } | |
3864 | |
3865 /* No point in recalculating for the other info elements, just copy the first one in */ | |
3866 var n = oSettings.aanFeatures.i; | |
3867 if ( n.length > 1 ) | |
3868 { | |
3869 var sInfo = nFirst.innerHTML; | |
3870 for ( var i=1, iLen=n.length ; i<iLen ; i++ ) | |
3871 { | |
3872 n[i].innerHTML = sInfo; | |
3873 } | |
3874 } | |
3875 } | |
3876 | |
3877 | |
3878 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * | |
3879 * Section - Feature: Length change | |
3880 */ | |
3881 | |
3882 /* | |
3883 * Function: _fnFeatureHtmlLength | |
3884 * Purpose: Generate the node required for user display length changing | |
3885 * Returns: node | |
3886 * Inputs: object:oSettings - dataTables settings object | |
3887 */ | |
3888 function _fnFeatureHtmlLength ( oSettings ) | |
3889 { | |
3890 /* This can be overruled by not using the _MENU_ var/macro in the language variable */ | |
3891 var sName = (oSettings.sTableId === "") ? "" : 'name="'+oSettings.sTableId+'_length"'; | |
3892 var sStdMenu = | |
3893 '<select size="1" '+sName+'>'+ | |
3894 '<option value="10">10</option>'+ | |
3895 '<option value="25">25</option>'+ | |
3896 '<option value="50">50</option>'+ | |
3897 '<option value="100">100</option>'+ | |
3898 '</select>'; | |
3899 | |
3900 var nLength = document.createElement( 'div' ); | |
3901 if ( oSettings.sTableId !== '' && typeof oSettings.aanFeatures.l == "undefined" ) | |
3902 { | |
3903 nLength.setAttribute( 'id', oSettings.sTableId+'_length' ); | |
3904 } | |
3905 nLength.className = oSettings.oClasses.sLength; | |
3906 nLength.innerHTML = oSettings.oLanguage.sLengthMenu.replace( '_MENU_', sStdMenu ); | |
3907 | |
3908 /* | |
3909 * Set the length to the current display length - thanks to Andrea Pavlovic for this fix, | |
3910 * and Stefan Skopnik for fixing the fix! | |
3911 */ | |
3912 $('select option[value="'+oSettings._iDisplayLength+'"]',nLength).attr("selected",true); | |
3913 | |
3914 $('select', nLength).change( function(e) { | |
3915 var iVal = $(this).val(); | |
3916 | |
3917 /* Update all other length options for the new display */ | |
3918 var n = oSettings.aanFeatures.l; | |
3919 for ( var i=0, iLen=n.length ; i<iLen ; i++ ) | |
3920 { | |
3921 if ( n[i] != this.parentNode ) | |
3922 { | |
3923 $('select', n[i]).val( iVal ); | |
3924 } | |
3925 } | |
3926 | |
3927 /* Redraw the table */ | |
3928 oSettings._iDisplayLength = parseInt(iVal, 10); | |
3929 _fnCalculateEnd( oSettings ); | |
3930 | |
3931 /* If we have space to show extra rows (backing up from the end point - then do so */ | |
3932 if ( oSettings._iDisplayEnd == oSettings.aiDisplay.length ) | |
3933 { | |
3934 oSettings._iDisplayStart = oSettings._iDisplayEnd - oSettings._iDisplayLength; | |
3935 if ( oSettings._iDisplayStart < 0 ) | |
3936 { | |
3937 oSettings._iDisplayStart = 0; | |
3938 } | |
3939 } | |
3940 | |
3941 if ( oSettings._iDisplayLength == -1 ) | |
3942 { | |
3943 oSettings._iDisplayStart = 0; | |
3944 } | |
3945 | |
3946 _fnDraw( oSettings ); | |
3947 } ); | |
3948 | |
3949 return nLength; | |
3950 } | |
3951 | |
3952 | |
3953 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * | |
3954 * Section - Feature: Processing incidator | |
3955 */ | |
3956 | |
3957 /* | |
3958 * Function: _fnFeatureHtmlProcessing | |
3959 * Purpose: Generate the node required for the processing node | |
3960 * Returns: node | |
3961 * Inputs: object:oSettings - dataTables settings object | |
3962 */ | |
3963 function _fnFeatureHtmlProcessing ( oSettings ) | |
3964 { | |
3965 var nProcessing = document.createElement( 'div' ); | |
3966 | |
3967 if ( oSettings.sTableId !== '' && typeof oSettings.aanFeatures.r == "undefined" ) | |
3968 { | |
3969 nProcessing.setAttribute( 'id', oSettings.sTableId+'_processing' ); | |
3970 } | |
3971 nProcessing.innerHTML = oSettings.oLanguage.sProcessing; | |
3972 nProcessing.className = oSettings.oClasses.sProcessing; | |
3973 oSettings.nTable.parentNode.insertBefore( nProcessing, oSettings.nTable ); | |
3974 | |
3975 return nProcessing; | |
3976 } | |
3977 | |
3978 /* | |
3979 * Function: _fnProcessingDisplay | |
3980 * Purpose: Display or hide the processing indicator | |
3981 * Returns: - | |
3982 * Inputs: object:oSettings - dataTables settings object | |
3983 * bool: | |
3984 * true - show the processing indicator | |
3985 * false - don't show | |
3986 */ | |
3987 function _fnProcessingDisplay ( oSettings, bShow ) | |
3988 { | |
3989 if ( oSettings.oFeatures.bProcessing ) | |
3990 { | |
3991 var an = oSettings.aanFeatures.r; | |
3992 for ( var i=0, iLen=an.length ; i<iLen ; i++ ) | |
3993 { | |
3994 an[i].style.visibility = bShow ? "visible" : "hidden"; | |
3995 } | |
3996 } | |
3997 } | |
3998 | |
3999 | |
4000 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * | |
4001 * Section - Support functions | |
4002 */ | |
4003 | |
4004 /* | |
4005 * Function: _fnVisibleToColumnIndex | |
4006 * Purpose: Covert the index of a visible column to the index in the data array (take account | |
4007 * of hidden columns) | |
4008 * Returns: int:i - the data index | |
4009 * Inputs: object:oSettings - dataTables settings object | |
4010 */ | |
4011 function _fnVisibleToColumnIndex( oSettings, iMatch ) | |
4012 { | |
4013 var iColumn = -1; | |
4014 | |
4015 for ( var i=0 ; i<oSettings.aoColumns.length ; i++ ) | |
4016 { | |
4017 if ( oSettings.aoColumns[i].bVisible === true ) | |
4018 { | |
4019 iColumn++; | |
4020 } | |
4021 | |
4022 if ( iColumn == iMatch ) | |
4023 { | |
4024 return i; | |
4025 } | |
4026 } | |
4027 | |
4028 return null; | |
4029 } | |
4030 | |
4031 /* | |
4032 * Function: _fnColumnIndexToVisible | |
4033 * Purpose: Covert the index of an index in the data array and convert it to the visible | |
4034 * column index (take account of hidden columns) | |
4035 * Returns: int:i - the data index | |
4036 * Inputs: object:oSettings - dataTables settings object | |
4037 */ | |
4038 function _fnColumnIndexToVisible( oSettings, iMatch ) | |
4039 { | |
4040 var iVisible = -1; | |
4041 for ( var i=0 ; i<oSettings.aoColumns.length ; i++ ) | |
4042 { | |
4043 if ( oSettings.aoColumns[i].bVisible === true ) | |
4044 { | |
4045 iVisible++; | |
4046 } | |
4047 | |
4048 if ( i == iMatch ) | |
4049 { | |
4050 return oSettings.aoColumns[i].bVisible === true ? iVisible : null; | |
4051 } | |
4052 } | |
4053 | |
4054 return null; | |
4055 } | |
4056 | |
4057 | |
4058 /* | |
4059 * Function: _fnNodeToDataIndex | |
4060 * Purpose: Take a TR element and convert it to an index in aoData | |
4061 * Returns: int:i - index if found, null if not | |
4062 * Inputs: object:s - dataTables settings object | |
4063 * node:n - the TR element to find | |
4064 */ | |
4065 function _fnNodeToDataIndex( s, n ) | |
4066 { | |
4067 for ( var i=0, iLen=s.aoData.length ; i<iLen ; i++ ) | |
4068 { | |
4069 if ( s.aoData[i] !== null && s.aoData[i].nTr == n ) | |
4070 { | |
4071 return i; | |
4072 } | |
4073 } | |
4074 return null; | |
4075 } | |
4076 | |
4077 /* | |
4078 * Function: _fnVisbleColumns | |
4079 * Purpose: Get the number of visible columns | |
4080 * Returns: int:i - the number of visible columns | |
4081 * Inputs: object:oS - dataTables settings object | |
4082 */ | |
4083 function _fnVisbleColumns( oS ) | |
4084 { | |
4085 var iVis = 0; | |
4086 for ( var i=0 ; i<oS.aoColumns.length ; i++ ) | |
4087 { | |
4088 if ( oS.aoColumns[i].bVisible === true ) | |
4089 { | |
4090 iVis++; | |
4091 } | |
4092 } | |
4093 return iVis; | |
4094 } | |
4095 | |
4096 /* | |
4097 * Function: _fnCalculateEnd | |
4098 * Purpose: Rcalculate the end point based on the start point | |
4099 * Returns: - | |
4100 * Inputs: object:oSettings - dataTables settings object | |
4101 */ | |
4102 function _fnCalculateEnd( oSettings ) | |
4103 { | |
4104 if ( oSettings.oFeatures.bPaginate === false ) | |
4105 { | |
4106 oSettings._iDisplayEnd = oSettings.aiDisplay.length; | |
4107 } | |
4108 else | |
4109 { | |
4110 /* Set the end point of the display - based on how many elements there are | |
4111 * still to display | |
4112 */ | |
4113 if ( oSettings._iDisplayStart + oSettings._iDisplayLength > oSettings.aiDisplay.length || | |
4114 oSettings._iDisplayLength == -1 ) | |
4115 { | |
4116 oSettings._iDisplayEnd = oSettings.aiDisplay.length; | |
4117 } | |
4118 else | |
4119 { | |
4120 oSettings._iDisplayEnd = oSettings._iDisplayStart + oSettings._iDisplayLength; | |
4121 } | |
4122 } | |
4123 } | |
4124 | |
4125 /* | |
4126 * Function: _fnConvertToWidth | |
4127 * Purpose: Convert a CSS unit width to pixels (e.g. 2em) | |
4128 * Returns: int:iWidth - width in pixels | |
4129 * Inputs: string:sWidth - width to be converted | |
4130 * node:nParent - parent to get the with for (required for | |
4131 * relative widths) - optional | |
4132 */ | |
4133 function _fnConvertToWidth ( sWidth, nParent ) | |
4134 { | |
4135 if ( !sWidth || sWidth === null || sWidth === '' ) | |
4136 { | |
4137 return 0; | |
4138 } | |
4139 | |
4140 if ( typeof nParent == "undefined" ) | |
4141 { | |
4142 nParent = document.getElementsByTagName('body')[0]; | |
4143 } | |
4144 | |
4145 var iWidth; | |
4146 var nTmp = document.createElement( "div" ); | |
4147 nTmp.style.width = sWidth; | |
4148 | |
4149 nParent.appendChild( nTmp ); | |
4150 iWidth = nTmp.offsetWidth; | |
4151 nParent.removeChild( nTmp ); | |
4152 | |
4153 return ( iWidth ); | |
4154 } | |
4155 | |
4156 /* | |
4157 * Function: _fnCalculateColumnWidths | |
4158 * Purpose: Calculate the width of columns for the table | |
4159 * Returns: - | |
4160 * Inputs: object:oSettings - dataTables settings object | |
4161 */ | |
4162 function _fnCalculateColumnWidths ( oSettings ) | |
4163 { | |
4164 var iTableWidth = oSettings.nTable.offsetWidth; | |
4165 var iTotalUserIpSize = 0; | |
4166 var iTmpWidth; | |
4167 var iVisibleColumns = 0; | |
4168 var iColums = oSettings.aoColumns.length; | |
4169 var i; | |
4170 var oHeaders = $('thead:eq(0)>th', oSettings.nTable); | |
4171 | |
4172 /* Convert any user input sizes into pixel sizes */ | |
4173 for ( i=0 ; i<iColums ; i++ ) | |
4174 { | |
4175 if ( oSettings.aoColumns[i].bVisible ) | |
4176 { | |
4177 iVisibleColumns++; | |
4178 | |
4179 if ( oSettings.aoColumns[i].sWidth !== null ) | |
4180 { | |
4181 iTmpWidth = _fnConvertToWidth( oSettings.aoColumns[i].sWidth, | |
4182 oSettings.nTable.parentNode ); | |
4183 | |
4184 /* Total up the user defined widths for later calculations */ | |
4185 iTotalUserIpSize += iTmpWidth; | |
4186 | |
4187 oSettings.aoColumns[i].sWidth = iTmpWidth+"px"; | |
4188 } | |
4189 } | |
4190 } | |
4191 | |
4192 /* If the number of columns in the DOM equals the number that we | |
4193 * have to process in dataTables, then we can use the offsets that are | |
4194 * created by the web-browser. No custom sizes can be set in order for | |
4195 * this to happen | |
4196 */ | |
4197 if ( iColums == oHeaders.length && iTotalUserIpSize === 0 && iVisibleColumns == iColums ) | |
4198 { | |
4199 for ( i=0 ; i<oSettings.aoColumns.length ; i++ ) | |
4200 { | |
4201 oSettings.aoColumns[i].sWidth = oHeaders[i].offsetWidth+"px"; | |
4202 } | |
4203 } | |
4204 else | |
4205 { | |
4206 /* Otherwise we are going to have to do some calculations to get | |
4207 * the width of each column. Construct a 1 row table with the maximum | |
4208 * string sizes in the data, and any user defined widths | |
4209 */ | |
4210 var nCalcTmp = oSettings.nTable.cloneNode( false ); | |
4211 nCalcTmp.setAttribute( "id", '' ); | |
4212 | |
4213 var sTableTmp = '<table class="'+nCalcTmp.className+'">'; | |
4214 var sCalcHead = "<tr>"; | |
4215 var sCalcHtml = "<tr>"; | |
4216 | |
4217 /* Construct a tempory table which we will inject (invisibly) into | |
4218 * the dom - to let the browser do all the hard word | |
4219 */ | |
4220 for ( i=0 ; i<iColums ; i++ ) | |
4221 { | |
4222 if ( oSettings.aoColumns[i].bVisible ) | |
4223 { | |
4224 sCalcHead += '<th>'+oSettings.aoColumns[i].sTitle+'</th>'; | |
4225 | |
4226 if ( oSettings.aoColumns[i].sWidth !== null ) | |
4227 { | |
4228 var sWidth = ''; | |
4229 if ( oSettings.aoColumns[i].sWidth !== null ) | |
4230 { | |
4231 sWidth = ' style="width:'+oSettings.aoColumns[i].sWidth+';"'; | |
4232 } | |
4233 | |
4234 sCalcHtml += '<td'+sWidth+' tag_index="'+i+'">'+fnGetMaxLenString( oSettings, i)+'</td>'; | |
4235 } | |
4236 else | |
4237 { | |
4238 sCalcHtml += '<td tag_index="'+i+'">'+fnGetMaxLenString( oSettings, i)+'</td>'; | |
4239 } | |
4240 } | |
4241 } | |
4242 | |
4243 sCalcHead += "</tr>"; | |
4244 sCalcHtml += "</tr>"; | |
4245 | |
4246 /* Create the tmp table node (thank you jQuery) */ | |
4247 nCalcTmp = $( sTableTmp + sCalcHead + sCalcHtml +'</table>' )[0]; | |
4248 nCalcTmp.style.width = iTableWidth + "px"; | |
4249 nCalcTmp.style.visibility = "hidden"; | |
4250 nCalcTmp.style.position = "absolute"; /* Try to aviod scroll bar */ | |
4251 | |
4252 oSettings.nTable.parentNode.appendChild( nCalcTmp ); | |
4253 | |
4254 var oNodes = $("tr:eq(1)>td", nCalcTmp); | |
4255 var iIndex; | |
4256 | |
4257 /* Gather in the browser calculated widths for the rows */ | |
4258 for ( i=0 ; i<oNodes.length ; i++ ) | |
4259 { | |
4260 iIndex = oNodes[i].getAttribute('tag_index'); | |
4261 | |
4262 var iContentWidth = $("td", nCalcTmp).eq(i).width(); | |
4263 var iSetWidth = oSettings.aoColumns[i].sWidth ? | |
4264 oSettings.aoColumns[i].sWidth.slice(0, -2) : 0; | |
4265 oSettings.aoColumns[iIndex].sWidth = Math.max(iContentWidth, iSetWidth) + "px"; | |
4266 } | |
4267 | |
4268 oSettings.nTable.parentNode.removeChild( nCalcTmp ); | |
4269 } | |
4270 } | |
4271 | |
4272 /* | |
4273 * Function: fnGetMaxLenString | |
4274 * Purpose: Get the maximum strlen for each data column | |
4275 * Returns: string: - max strlens for each column | |
4276 * Inputs: object:oSettings - dataTables settings object | |
4277 * int:iCol - column of interest | |
4278 */ | |
4279 function fnGetMaxLenString( oSettings, iCol ) | |
4280 { | |
4281 var iMax = 0; | |
4282 var iMaxIndex = -1; | |
4283 | |
4284 for ( var i=0 ; i<oSettings.aoData.length ; i++ ) | |
4285 { | |
4286 if ( oSettings.aoData[i]._aData[iCol].length > iMax ) | |
4287 { | |
4288 iMax = oSettings.aoData[i]._aData[iCol].length; | |
4289 iMaxIndex = i; | |
4290 } | |
4291 } | |
4292 | |
4293 if ( iMaxIndex >= 0 ) | |
4294 { | |
4295 return oSettings.aoData[iMaxIndex]._aData[iCol]; | |
4296 } | |
4297 return ''; | |
4298 } | |
4299 | |
4300 /* | |
4301 * Function: _fnArrayCmp | |
4302 * Purpose: Compare two arrays | |
4303 * Returns: 0 if match, 1 if length is different, 2 if no match | |
4304 * Inputs: array:aArray1 - first array | |
4305 * array:aArray2 - second array | |
4306 */ | |
4307 function _fnArrayCmp( aArray1, aArray2 ) | |
4308 { | |
4309 if ( aArray1.length != aArray2.length ) | |
4310 { | |
4311 return 1; | |
4312 } | |
4313 | |
4314 for ( var i=0 ; i<aArray1.length ; i++ ) | |
4315 { | |
4316 if ( aArray1[i] != aArray2[i] ) | |
4317 { | |
4318 return 2; | |
4319 } | |
4320 } | |
4321 | |
4322 return 0; | |
4323 } | |
4324 | |
4325 /* | |
4326 * Function: _fnDetectType | |
4327 * Purpose: Get the sort type based on an input string | |
4328 * Returns: string: - type (defaults to 'string' if no type can be detected) | |
4329 * Inputs: string:sData - data we wish to know the type of | |
4330 * Notes: This function makes use of the DataTables plugin objct _oExt | |
4331 * (.aTypes) such that new types can easily be added. | |
4332 */ | |
4333 function _fnDetectType( sData ) | |
4334 { | |
4335 var aTypes = _oExt.aTypes; | |
4336 var iLen = aTypes.length; | |
4337 | |
4338 for ( var i=0 ; i<iLen ; i++ ) | |
4339 { | |
4340 var sType = aTypes[i]( sData ); | |
4341 if ( sType !== null ) | |
4342 { | |
4343 return sType; | |
4344 } | |
4345 } | |
4346 | |
4347 return 'string'; | |
4348 } | |
4349 | |
4350 /* | |
4351 * Function: _fnSettingsFromNode | |
4352 * Purpose: Return the settings object for a particular table | |
4353 * Returns: object: Settings object - or null if not found | |
4354 * Inputs: node:nTable - table we are using as a dataTable | |
4355 */ | |
4356 function _fnSettingsFromNode ( nTable ) | |
4357 { | |
4358 for ( var i=0 ; i<_aoSettings.length ; i++ ) | |
4359 { | |
4360 if ( _aoSettings[i].nTable == nTable ) | |
4361 { | |
4362 return _aoSettings[i]; | |
4363 } | |
4364 } | |
4365 | |
4366 return null; | |
4367 } | |
4368 | |
4369 /* | |
4370 * Function: _fnGetDataMaster | |
4371 * Purpose: Return an array with the full table data | |
4372 * Returns: array array:aData - Master data array | |
4373 * Inputs: object:oSettings - dataTables settings object | |
4374 */ | |
4375 function _fnGetDataMaster ( oSettings ) | |
4376 { | |
4377 var aData = []; | |
4378 var iLen = oSettings.aoData.length; | |
4379 for ( var i=0 ; i<iLen; i++ ) | |
4380 { | |
4381 if ( oSettings.aoData[i] === null ) | |
4382 { | |
4383 aData.push( null ); | |
4384 } | |
4385 else | |
4386 { | |
4387 aData.push( oSettings.aoData[i]._aData ); | |
4388 } | |
4389 } | |
4390 return aData; | |
4391 } | |
4392 | |
4393 /* | |
4394 * Function: _fnGetTrNodes | |
4395 * Purpose: Return an array with the TR nodes for the table | |
4396 * Returns: array: - TR array | |
4397 * Inputs: object:oSettings - dataTables settings object | |
4398 */ | |
4399 function _fnGetTrNodes ( oSettings ) | |
4400 { | |
4401 var aNodes = []; | |
4402 var iLen = oSettings.aoData.length; | |
4403 for ( var i=0 ; i<iLen ; i++ ) | |
4404 { | |
4405 if ( oSettings.aoData[i] === null ) | |
4406 { | |
4407 aNodes.push( null ); | |
4408 } | |
4409 else | |
4410 { | |
4411 aNodes.push( oSettings.aoData[i].nTr ); | |
4412 } | |
4413 } | |
4414 return aNodes; | |
4415 } | |
4416 | |
4417 /* | |
4418 * Function: _fnGetTdNodes | |
4419 * Purpose: Return an array with the TD nodes for the table | |
4420 * Returns: array: - TD array | |
4421 * Inputs: object:oSettings - dataTables settings object | |
4422 */ | |
4423 function _fnGetTdNodes ( oSettings ) | |
4424 { | |
4425 var nTrs = _fnGetTrNodes( oSettings ); | |
4426 var nTds = [], nTd; | |
4427 var anReturn = []; | |
4428 var iCorrector; | |
4429 var iRow, iRows, iColumn, iColumns; | |
4430 | |
4431 for ( iRow=0, iRows=nTrs.length ; iRow<iRows ; iRow++ ) | |
4432 { | |
4433 nTds = []; | |
4434 for ( iColumn=0, iColumns=nTrs[iRow].childNodes.length ; iColumn<iColumns ; iColumn++ ) | |
4435 { | |
4436 nTd = nTrs[iRow].childNodes[iColumn]; | |
4437 if ( nTd.nodeName == "TD" ) | |
4438 { | |
4439 nTds.push( nTd ); | |
4440 } | |
4441 } | |
4442 | |
4443 iCorrector = 0; | |
4444 for ( iColumn=0, iColumns=oSettings.aoColumns.length ; iColumn<iColumns ; iColumn++ ) | |
4445 { | |
4446 if ( oSettings.aoColumns[iColumn].bVisible ) | |
4447 { | |
4448 anReturn.push( nTds[iColumn-iCorrector] ); | |
4449 } | |
4450 else | |
4451 { | |
4452 anReturn.push( oSettings.aoData[iRow]._anHidden[iColumn] ); | |
4453 iCorrector++; | |
4454 } | |
4455 } | |
4456 } | |
4457 return anReturn; | |
4458 } | |
4459 | |
4460 /* | |
4461 * Function: _fnEscapeRegex | |
4462 * Purpose: scape a string stuch that it can be used in a regular expression | |
4463 * Returns: string: - escaped string | |
4464 * Inputs: string:sVal - string to escape | |
4465 */ | |
4466 function _fnEscapeRegex ( sVal ) | |
4467 { | |
4468 var acEscape = [ '/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\', '$', '^' ]; | |
4469 var reReplace = new RegExp( '(\\' + acEscape.join('|\\') + ')', 'g' ); | |
4470 return sVal.replace(reReplace, '\\$1'); | |
4471 } | |
4472 | |
4473 /* | |
4474 * Function: _fnReOrderIndex | |
4475 * Purpose: Figure out how to reorder a display list | |
4476 * Returns: array int:aiReturn - index list for reordering | |
4477 * Inputs: object:oSettings - dataTables settings object | |
4478 */ | |
4479 function _fnReOrderIndex ( oSettings, sColumns ) | |
4480 { | |
4481 var aColumns = sColumns.split(','); | |
4482 var aiReturn = []; | |
4483 | |
4484 for ( var i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ ) | |
4485 { | |
4486 for ( var j=0 ; j<iLen ; j++ ) | |
4487 { | |
4488 if ( oSettings.aoColumns[i].sName == aColumns[j] ) | |
4489 { | |
4490 aiReturn.push( j ); | |
4491 break; | |
4492 } | |
4493 } | |
4494 } | |
4495 | |
4496 return aiReturn; | |
4497 } | |
4498 | |
4499 /* | |
4500 * Function: _fnColumnOrdering | |
4501 * Purpose: Get the column ordering that DataTables expects | |
4502 * Returns: string: - comma separated list of names | |
4503 * Inputs: object:oSettings - dataTables settings object | |
4504 */ | |
4505 function _fnColumnOrdering ( oSettings ) | |
4506 { | |
4507 var sNames = ''; | |
4508 for ( var i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ ) | |
4509 { | |
4510 sNames += oSettings.aoColumns[i].sName+','; | |
4511 } | |
4512 if ( sNames.length == iLen ) | |
4513 { | |
4514 return ""; | |
4515 } | |
4516 return sNames.slice(0, -1); | |
4517 } | |
4518 | |
4519 /* | |
4520 * Function: _fnClearTable | |
4521 * Purpose: Nuke the table | |
4522 * Returns: - | |
4523 * Inputs: object:oSettings - dataTables settings object | |
4524 */ | |
4525 function _fnClearTable( oSettings ) | |
4526 { | |
4527 oSettings.aoData.length = 0; | |
4528 oSettings.aiDisplayMaster.length = 0; | |
4529 oSettings.aiDisplay.length = 0; | |
4530 _fnCalculateEnd( oSettings ); | |
4531 } | |
4532 | |
4533 /* | |
4534 * Function: _fnSaveState | |
4535 * Purpose: Save the state of a table in a cookie such that the page can be reloaded | |
4536 * Returns: - | |
4537 * Inputs: object:oSettings - dataTables settings object | |
4538 */ | |
4539 function _fnSaveState ( oSettings ) | |
4540 { | |
4541 if ( !oSettings.oFeatures.bStateSave ) | |
4542 { | |
4543 return; | |
4544 } | |
4545 | |
4546 /* Store the interesting variables */ | |
4547 var i; | |
4548 var sValue = "{"; | |
4549 sValue += '"iStart": '+oSettings._iDisplayStart+','; | |
4550 sValue += '"iEnd": '+oSettings._iDisplayEnd+','; | |
4551 sValue += '"iLength": '+oSettings._iDisplayLength+','; | |
4552 sValue += '"sFilter": "'+oSettings.oPreviousSearch.sSearch.replace('"','\\"')+'",'; | |
4553 sValue += '"sFilterEsc": '+oSettings.oPreviousSearch.bEscapeRegex+','; | |
4554 | |
4555 sValue += '"aaSorting": [ '; | |
4556 for ( i=0 ; i<oSettings.aaSorting.length ; i++ ) | |
4557 { | |
4558 sValue += "["+oSettings.aaSorting[i][0]+",'"+oSettings.aaSorting[i][1]+"'],"; | |
4559 } | |
4560 sValue = sValue.substring(0, sValue.length-1); | |
4561 sValue += "],"; | |
4562 | |
4563 sValue += '"aaSearchCols": [ '; | |
4564 for ( i=0 ; i<oSettings.aoPreSearchCols.length ; i++ ) | |
4565 { | |
4566 sValue += "['"+oSettings.aoPreSearchCols[i].sSearch.replace("'","\'")+ | |
4567 "',"+oSettings.aoPreSearchCols[i].bEscapeRegex+"],"; | |
4568 } | |
4569 sValue = sValue.substring(0, sValue.length-1); | |
4570 sValue += "],"; | |
4571 | |
4572 sValue += '"abVisCols": [ '; | |
4573 for ( i=0 ; i<oSettings.aoColumns.length ; i++ ) | |
4574 { | |
4575 sValue += oSettings.aoColumns[i].bVisible+","; | |
4576 } | |
4577 sValue = sValue.substring(0, sValue.length-1); | |
4578 sValue += "]"; | |
4579 | |
4580 sValue += "}"; | |
4581 _fnCreateCookie( "SpryMedia_DataTables_"+oSettings.sInstance, sValue, | |
4582 oSettings.iCookieDuration ); | |
4583 } | |
4584 | |
4585 /* | |
4586 * Function: _fnLoadState | |
4587 * Purpose: Attempt to load a saved table state from a cookie | |
4588 * Returns: - | |
4589 * Inputs: object:oSettings - dataTables settings object | |
4590 * object:oInit - DataTables init object so we can override settings | |
4591 */ | |
4592 function _fnLoadState ( oSettings, oInit ) | |
4593 { | |
4594 if ( !oSettings.oFeatures.bStateSave ) | |
4595 { | |
4596 return; | |
4597 } | |
4598 | |
4599 var oData; | |
4600 var sData = _fnReadCookie( "SpryMedia_DataTables_"+oSettings.sInstance ); | |
4601 if ( sData !== null && sData !== '' ) | |
4602 { | |
4603 /* Try/catch the JSON eval - if it is bad then we ignore it */ | |
4604 try | |
4605 { | |
4606 /* Use the JSON library for safety - if it is available */ | |
4607 if ( typeof JSON == 'object' && typeof JSON.parse == 'function' ) | |
4608 { | |
4609 /* DT 1.4.0 used single quotes for a string - JSON.parse doesn't allow this and throws | |
4610 * an error. So for now we can do this. This can be removed in future it is just to | |
4611 * allow the tranfrer to 1.4.1+ to occur | |
4612 */ | |
4613 oData = JSON.parse( sData.replace(/'/g, '"') ); | |
4614 } | |
4615 else | |
4616 { | |
4617 oData = eval( '('+sData+')' ); | |
4618 } | |
4619 } | |
4620 catch( e ) | |
4621 { | |
4622 return; | |
4623 } | |
4624 | |
4625 /* Restore key features */ | |
4626 oSettings._iDisplayStart = oData.iStart; | |
4627 oSettings.iInitDisplayStart = oData.iStart; | |
4628 oSettings._iDisplayEnd = oData.iEnd; | |
4629 oSettings._iDisplayLength = oData.iLength; | |
4630 oSettings.oPreviousSearch.sSearch = oData.sFilter; | |
4631 oSettings.aaSorting = oData.aaSorting.slice(); | |
4632 oSettings.saved_aaSorting = oData.aaSorting.slice(); | |
4633 | |
4634 /* Search filtering - global reference added in 1.4.1 */ | |
4635 if ( typeof oData.sFilterEsc != 'undefined' ) | |
4636 { | |
4637 oSettings.oPreviousSearch.bEscapeRegex = oData.sFilterEsc; | |
4638 } | |
4639 | |
4640 /* Column filtering - added in 1.5.0 beta 6 */ | |
4641 if ( typeof oData.aaSearchCols != 'undefined' ) | |
4642 { | |
4643 for ( var i=0 ; i<oData.aaSearchCols.length ; i++ ) | |
4644 { | |
4645 oSettings.aoPreSearchCols[i] = { | |
4646 "sSearch": oData.aaSearchCols[i][0], | |
4647 "bEscapeRegex": oData.aaSearchCols[i][1] | |
4648 }; | |
4649 } | |
4650 } | |
4651 | |
4652 /* Column visibility state - added in 1.5.0 beta 10 */ | |
4653 if ( typeof oData.abVisCols != 'undefined' ) | |
4654 { | |
4655 /* Pass back visibiliy settings to the init handler, but to do not here override | |
4656 * the init object that the user might have passed in | |
4657 */ | |
4658 oInit.saved_aoColumns = []; | |
4659 for ( i=0 ; i<oData.abVisCols.length ; i++ ) | |
4660 { | |
4661 oInit.saved_aoColumns[i] = {}; | |
4662 oInit.saved_aoColumns[i].bVisible = oData.abVisCols[i]; | |
4663 } | |
4664 } | |
4665 } | |
4666 } | |
4667 | |
4668 /* | |
4669 * Function: _fnCreateCookie | |
4670 * Purpose: Create a new cookie with a value to store the state of a table | |
4671 * Returns: - | |
4672 * Inputs: string:sName - name of the cookie to create | |
4673 * string:sValue - the value the cookie should take | |
4674 * int:iSecs - duration of the cookie | |
4675 */ | |
4676 function _fnCreateCookie ( sName, sValue, iSecs ) | |
4677 { | |
4678 var date = new Date(); | |
4679 date.setTime( date.getTime()+(iSecs*1000) ); | |
4680 | |
4681 /* | |
4682 * Shocking but true - it would appear IE has major issues with having the path being | |
4683 * set to anything but root. We need the cookie to be available based on the path, so we | |
4684 * have to append the pathname to the cookie name. Appalling. | |
4685 */ | |
4686 sName += '_'+window.location.pathname.replace(/[\/:]/g,"").toLowerCase(); | |
4687 | |
4688 document.cookie = sName+"="+encodeURIComponent(sValue)+ | |
4689 "; expires="+date.toGMTString()+"; path=/"; | |
4690 } | |
4691 | |
4692 /* | |
4693 * Function: _fnReadCookie | |
4694 * Purpose: Read an old cookie to get a cookie with an old table state | |
4695 * Returns: string: - contents of the cookie - or null if no cookie with that name found | |
4696 * Inputs: string:sName - name of the cookie to read | |
4697 */ | |
4698 function _fnReadCookie ( sName ) | |
4699 { | |
4700 var sNameEQ = sName +'_'+ window.location.pathname.replace(/[\/:]/g,"").toLowerCase() + "="; | |
4701 var sCookieContents = document.cookie.split(';'); | |
4702 | |
4703 for( var i=0 ; i<sCookieContents.length ; i++ ) | |
4704 { | |
4705 var c = sCookieContents[i]; | |
4706 | |
4707 while (c.charAt(0)==' ') | |
4708 { | |
4709 c = c.substring(1,c.length); | |
4710 } | |
4711 | |
4712 if (c.indexOf(sNameEQ) === 0) | |
4713 { | |
4714 return decodeURIComponent( c.substring(sNameEQ.length,c.length) ); | |
4715 } | |
4716 } | |
4717 return null; | |
4718 } | |
4719 | |
4720 /* | |
4721 * Function: _fnGetUniqueThs | |
4722 * Purpose: Get an array of unique th elements, one for each column | |
4723 * Returns: array node:aReturn - list of unique ths | |
4724 * Inputs: node:nThead - The thead element for the table | |
4725 */ | |
4726 function _fnGetUniqueThs ( nThead ) | |
4727 { | |
4728 var nTrs = nThead.getElementsByTagName('tr'); | |
4729 | |
4730 /* Nice simple case */ | |
4731 if ( nTrs.length == 1 ) | |
4732 { | |
4733 return nTrs[0].getElementsByTagName('th'); | |
4734 } | |
4735 | |
4736 /* Otherwise we need to figure out the layout array to get the nodes */ | |
4737 var aLayout = [], aReturn = []; | |
4738 var ROWSPAN = 2, COLSPAN = 3, TDELEM = 4; | |
4739 var i, j, k, iLen, jLen, iColumnShifted; | |
4740 var fnShiftCol = function ( a, i, j ) { | |
4741 while ( typeof a[i][j] != 'undefined' ) { | |
4742 j++; | |
4743 } | |
4744 return j; | |
4745 }; | |
4746 var fnAddRow = function ( i ) { | |
4747 if ( typeof aLayout[i] == 'undefined' ) { | |
4748 aLayout[i] = []; | |
4749 } | |
4750 }; | |
4751 | |
4752 /* Calculate a layout array */ | |
4753 for ( i=0, iLen=nTrs.length ; i<iLen ; i++ ) | |
4754 { | |
4755 fnAddRow( i ); | |
4756 var iColumn = 0; | |
4757 var nTds = []; | |
4758 | |
4759 for ( j=0, jLen=nTrs[i].childNodes.length ; j<jLen ; j++ ) | |
4760 { | |
4761 if ( nTrs[i].childNodes[j].nodeName == "TD" || nTrs[i].childNodes[j].nodeName == "TH" ) | |
4762 { | |
4763 nTds.push( nTrs[i].childNodes[j] ); | |
4764 } | |
4765 } | |
4766 | |
4767 for ( j=0, jLen=nTds.length ; j<jLen ; j++ ) | |
4768 { | |
4769 var iColspan = nTds[j].getAttribute('colspan') * 1; | |
4770 var iRowspan = nTds[j].getAttribute('rowspan') * 1; | |
4771 | |
4772 if ( !iColspan || iColspan===0 || iColspan===1 ) | |
4773 { | |
4774 iColumnShifted = fnShiftCol( aLayout, i, iColumn ); | |
4775 aLayout[i][iColumnShifted] = (nTds[j].nodeName=="TD") ? TDELEM : nTds[j]; | |
4776 if ( iRowspan || iRowspan===0 || iRowspan===1 ) | |
4777 { | |
4778 for ( k=1 ; k<iRowspan ; k++ ) | |
4779 { | |
4780 fnAddRow( i+k ); | |
4781 aLayout[i+k][iColumnShifted] = ROWSPAN; | |
4782 } | |
4783 } | |
4784 iColumn++; | |
4785 } | |
4786 else | |
4787 { | |
4788 iColumnShifted = fnShiftCol( aLayout, i, iColumn ); | |
4789 for ( k=0 ; k<iColspan ; k++ ) | |
4790 { | |
4791 aLayout[i][iColumnShifted+k] = COLSPAN; | |
4792 } | |
4793 iColumn += iColspan; | |
4794 } | |
4795 } | |
4796 } | |
4797 | |
4798 /* Convert the layout array into a node array | |
4799 * Note the use of aLayout[0] in the outloop, we want the outer loop to occur the same | |
4800 * number of times as there are columns. Unusual having nested loops this way around | |
4801 * but is what we need here. | |
4802 */ | |
4803 for ( i=0, iLen=aLayout[0].length ; i<iLen ; i++ ) | |
4804 { | |
4805 for ( j=0, jLen=aLayout.length ; j<jLen ; j++ ) | |
4806 { | |
4807 if ( typeof aLayout[j][i] == 'object' ) | |
4808 { | |
4809 aReturn.push( aLayout[j][i] ); | |
4810 } | |
4811 } | |
4812 } | |
4813 | |
4814 return aReturn; | |
4815 } | |
4816 | |
4817 /* | |
4818 * Function: _fnMap | |
4819 * Purpose: See if a property is defined on one object, if so assign it to the other object | |
4820 * Returns: - (done by reference) | |
4821 * Inputs: object:oRet - target object | |
4822 * object:oSrc - source object | |
4823 * string:sName - property | |
4824 * string:sMappedName - name to map too - optional, sName used if not given | |
4825 */ | |
4826 function _fnMap( oRet, oSrc, sName, sMappedName ) | |
4827 { | |
4828 if ( typeof sMappedName == 'undefined' ) | |
4829 { | |
4830 sMappedName = sName; | |
4831 } | |
4832 if ( typeof oSrc[sName] != 'undefined' ) | |
4833 { | |
4834 oRet[sMappedName] = oSrc[sName]; | |
4835 } | |
4836 } | |
4837 | |
4838 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * | |
4839 * Section - API | |
4840 * | |
4841 * I'm not overly happy with this solution - I'd much rather that there was a way of getting | |
4842 * a list of all the private functions and do what we need to dynamically - but that doesn't | |
4843 * appear to be possible. Bonkers. A better solution would be to provide a 'bind' type object | |
4844 * To do - bind type method in DTs 2.x. | |
4845 */ | |
4846 this.oApi._fnInitalise = _fnInitalise; | |
4847 this.oApi._fnLanguageProcess = _fnLanguageProcess; | |
4848 this.oApi._fnAddColumn = _fnAddColumn; | |
4849 this.oApi._fnAddData = _fnAddData; | |
4850 this.oApi._fnGatherData = _fnGatherData; | |
4851 this.oApi._fnDrawHead = _fnDrawHead; | |
4852 this.oApi._fnDraw = _fnDraw; | |
4853 this.oApi._fnAjaxUpdate = _fnAjaxUpdate; | |
4854 this.oApi._fnAddOptionsHtml = _fnAddOptionsHtml; | |
4855 this.oApi._fnFeatureHtmlFilter = _fnFeatureHtmlFilter; | |
4856 this.oApi._fnFeatureHtmlInfo = _fnFeatureHtmlInfo; | |
4857 this.oApi._fnFeatureHtmlPaginate = _fnFeatureHtmlPaginate; | |
4858 this.oApi._fnPageChange = _fnPageChange; | |
4859 this.oApi._fnFeatureHtmlLength = _fnFeatureHtmlLength; | |
4860 this.oApi._fnFeatureHtmlProcessing = _fnFeatureHtmlProcessing; | |
4861 this.oApi._fnProcessingDisplay = _fnProcessingDisplay; | |
4862 this.oApi._fnFilterComplete = _fnFilterComplete; | |
4863 this.oApi._fnFilterColumn = _fnFilterColumn; | |
4864 this.oApi._fnFilter = _fnFilter; | |
4865 this.oApi._fnSortingClasses = _fnSortingClasses; | |
4866 this.oApi._fnVisibleToColumnIndex = _fnVisibleToColumnIndex; | |
4867 this.oApi._fnColumnIndexToVisible = _fnColumnIndexToVisible; | |
4868 this.oApi._fnNodeToDataIndex = _fnNodeToDataIndex; | |
4869 this.oApi._fnVisbleColumns = _fnVisbleColumns; | |
4870 this.oApi._fnBuildSearchArray = _fnBuildSearchArray; | |
4871 this.oApi._fnDataToSearch = _fnDataToSearch; | |
4872 this.oApi._fnCalculateEnd = _fnCalculateEnd; | |
4873 this.oApi._fnConvertToWidth = _fnConvertToWidth; | |
4874 this.oApi._fnCalculateColumnWidths = _fnCalculateColumnWidths; | |
4875 this.oApi._fnArrayCmp = _fnArrayCmp; | |
4876 this.oApi._fnDetectType = _fnDetectType; | |
4877 this.oApi._fnGetDataMaster = _fnGetDataMaster; | |
4878 this.oApi._fnGetTrNodes = _fnGetTrNodes; | |
4879 this.oApi._fnGetTdNodes = _fnGetTdNodes; | |
4880 this.oApi._fnEscapeRegex = _fnEscapeRegex; | |
4881 this.oApi._fnReOrderIndex = _fnReOrderIndex; | |
4882 this.oApi._fnColumnOrdering = _fnColumnOrdering; | |
4883 this.oApi._fnClearTable = _fnClearTable; | |
4884 this.oApi._fnSaveState = _fnSaveState; | |
4885 this.oApi._fnLoadState = _fnLoadState; | |
4886 this.oApi._fnCreateCookie = _fnCreateCookie; | |
4887 this.oApi._fnReadCookie = _fnReadCookie; | |
4888 this.oApi._fnGetUniqueThs = _fnGetUniqueThs; | |
4889 this.oApi._fnReDraw = _fnReDraw; | |
4890 | |
4891 /* Want to be able to reference "this" inside the this.each function */ | |
4892 var _that = this; | |
4893 | |
4894 | |
4895 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * | |
4896 * Section - Constructor | |
4897 */ | |
4898 return this.each(function() | |
4899 { | |
4900 var i=0, iLen, j, jLen; | |
4901 | |
4902 /* Sanity check that we are not re-initialising a table - if we are, alert an error */ | |
4903 for ( i=0, iLen=_aoSettings.length ; i<iLen ; i++ ) | |
4904 { | |
4905 if ( _aoSettings[i].nTable == this ) | |
4906 { | |
4907 alert( "DataTables warning: Unable to re-initialise DataTable. "+ | |
4908 "Please use the API to make any configuration changes required." ); | |
4909 return _aoSettings[i]; | |
4910 } | |
4911 } | |
4912 | |
4913 /* Make a complete and independent copy of the settings object */ | |
4914 var oSettings = new classSettings(); | |
4915 _aoSettings.push( oSettings ); | |
4916 | |
4917 var bInitHandedOff = false; | |
4918 var bUsePassedData = false; | |
4919 | |
4920 /* Set the id */ | |
4921 var sId = this.getAttribute( 'id' ); | |
4922 if ( sId !== null ) | |
4923 { | |
4924 oSettings.sTableId = sId; | |
4925 oSettings.sInstance = sId; | |
4926 } | |
4927 else | |
4928 { | |
4929 oSettings.sInstance = _oExt._oExternConfig.iNextUnique ++; | |
4930 } | |
4931 | |
4932 /* Set the table node */ | |
4933 oSettings.nTable = this; | |
4934 | |
4935 /* Bind the API functions to the settings, so we can perform actions whenever oSettings is | |
4936 * available | |
4937 */ | |
4938 oSettings.oApi = _that.oApi; | |
4939 | |
4940 /* Store the features that we have available */ | |
4941 if ( typeof oInit != 'undefined' && oInit !== null ) | |
4942 { | |
4943 _fnMap( oSettings.oFeatures, oInit, "bPaginate" ); | |
4944 _fnMap( oSettings.oFeatures, oInit, "bLengthChange" ); | |
4945 _fnMap( oSettings.oFeatures, oInit, "bFilter" ); | |
4946 _fnMap( oSettings.oFeatures, oInit, "bSort" ); | |
4947 _fnMap( oSettings.oFeatures, oInit, "bInfo" ); | |
4948 _fnMap( oSettings.oFeatures, oInit, "bProcessing" ); | |
4949 _fnMap( oSettings.oFeatures, oInit, "bAutoWidth" ); | |
4950 _fnMap( oSettings.oFeatures, oInit, "bSortClasses" ); | |
4951 _fnMap( oSettings.oFeatures, oInit, "bServerSide" ); | |
4952 _fnMap( oSettings, oInit, "asStripClasses" ); | |
4953 _fnMap( oSettings, oInit, "fnRowCallback" ); | |
4954 _fnMap( oSettings, oInit, "fnHeaderCallback" ); | |
4955 _fnMap( oSettings, oInit, "fnFooterCallback" ); | |
4956 _fnMap( oSettings, oInit, "fnInitComplete" ); | |
4957 _fnMap( oSettings, oInit, "fnServerData" ); | |
4958 _fnMap( oSettings, oInit, "aaSorting" ); | |
4959 _fnMap( oSettings, oInit, "aaSortingFixed" ); | |
4960 _fnMap( oSettings, oInit, "sPaginationType" ); | |
4961 _fnMap( oSettings, oInit, "sAjaxSource" ); | |
4962 _fnMap( oSettings, oInit, "iCookieDuration" ); | |
4963 _fnMap( oSettings, oInit, "sDom" ); | |
4964 _fnMap( oSettings, oInit, "oSearch", "oPreviousSearch" ); | |
4965 _fnMap( oSettings, oInit, "aoSearchCols", "aoPreSearchCols" ); | |
4966 _fnMap( oSettings, oInit, "iDisplayLength", "_iDisplayLength" ); | |
4967 _fnMap( oSettings, oInit, "bJQueryUI", "bJUI" ); | |
4968 | |
4969 if ( typeof oInit.fnDrawCallback == 'function' ) | |
4970 { | |
4971 /* Add user given callback function to array */ | |
4972 oSettings.aoDrawCallback.push( { | |
4973 "fn": oInit.fnDrawCallback, | |
4974 "sName": "user" | |
4975 } ); | |
4976 } | |
4977 | |
4978 if ( oSettings.oFeatures.bServerSide && oSettings.oFeatures.bSort && | |
4979 oSettings.oFeatures.bSortClasses ) | |
4980 { | |
4981 /* Enable sort classes for server-side processing. Safe to do it here, since server-side | |
4982 * processing must be enabled by the developer | |
4983 */ | |
4984 oSettings.aoDrawCallback.push( { | |
4985 "fn": _fnSortingClasses, | |
4986 "sName": "server_side_sort_classes" | |
4987 } ); | |
4988 } | |
4989 | |
4990 if ( typeof oInit.bJQueryUI != 'undefined' && oInit.bJQueryUI ) | |
4991 { | |
4992 /* Use the JUI classes object for display. You could clone the oStdClasses object if | |
4993 * you want to have multiple tables with multiple independent classes | |
4994 */ | |
4995 oSettings.oClasses = _oExt.oJUIClasses; | |
4996 | |
4997 if ( typeof oInit.sDom == 'undefined' ) | |
4998 { | |
4999 /* Set the DOM to use a layout suitable for jQuery UI's theming */ | |
5000 oSettings.sDom = '<"H"lfr>t<"F"ip>'; | |
5001 } | |
5002 } | |
5003 | |
5004 if ( typeof oInit.iDisplayStart != 'undefined' && | |
5005 typeof oSettings.iInitDisplayStart == 'undefined' ) | |
5006 { | |
5007 /* Display start point, taking into account the save saving */ | |
5008 oSettings.iInitDisplayStart = oInit.iDisplayStart; | |
5009 oSettings._iDisplayStart = oInit.iDisplayStart; | |
5010 } | |
5011 | |
5012 /* Must be done after everything which can be overridden by a cookie! */ | |
5013 if ( typeof oInit.bStateSave != 'undefined' ) | |
5014 { | |
5015 oSettings.oFeatures.bStateSave = oInit.bStateSave; | |
5016 _fnLoadState( oSettings, oInit ); | |
5017 oSettings.aoDrawCallback.push( { | |
5018 "fn": _fnSaveState, | |
5019 "sName": "state_save" | |
5020 } ); | |
5021 } | |
5022 | |
5023 if ( typeof oInit.aaData != 'undefined' ) | |
5024 { | |
5025 bUsePassedData = true; | |
5026 } | |
5027 | |
5028 /* Backwards compatability */ | |
5029 /* aoColumns / aoData - remove at some point... */ | |
5030 if ( typeof oInit != 'undefined' && typeof oInit.aoData != 'undefined' ) | |
5031 { | |
5032 oInit.aoColumns = oInit.aoData; | |
5033 } | |
5034 | |
5035 /* Language definitions */ | |
5036 if ( typeof oInit.oLanguage != 'undefined' ) | |
5037 { | |
5038 if ( typeof oInit.oLanguage.sUrl != 'undefined' && oInit.oLanguage.sUrl !== "" ) | |
5039 { | |
5040 /* Get the language definitions from a file */ | |
5041 oSettings.oLanguage.sUrl = oInit.oLanguage.sUrl; | |
5042 $.getJSON( oSettings.oLanguage.sUrl, null, function( json ) { | |
5043 _fnLanguageProcess( oSettings, json, true ); } ); | |
5044 bInitHandedOff = true; | |
5045 } | |
5046 else | |
5047 { | |
5048 _fnLanguageProcess( oSettings, oInit.oLanguage, false ); | |
5049 } | |
5050 } | |
5051 /* Warning: The _fnLanguageProcess function is async to the remainder of this function due | |
5052 * to the XHR. We use _bInitialised in _fnLanguageProcess() to check this the processing | |
5053 * below is complete. The reason for spliting it like this is optimisation - we can fire | |
5054 * off the XHR (if needed) and then continue processing the data. | |
5055 */ | |
5056 } | |
5057 else | |
5058 { | |
5059 /* Create a dummy object for quick manipulation later on. */ | |
5060 oInit = {}; | |
5061 } | |
5062 | |
5063 /* Add the strip classes now that we know which classes to apply - unless overruled */ | |
5064 if ( typeof oInit.asStripClasses == 'undefined' ) | |
5065 { | |
5066 oSettings.asStripClasses.push( oSettings.oClasses.sStripOdd ); | |
5067 oSettings.asStripClasses.push( oSettings.oClasses.sStripEven ); | |
5068 } | |
5069 | |
5070 /* See if we should load columns automatically or use defined ones - a bit messy this... */ | |
5071 var nThead = this.getElementsByTagName('thead'); | |
5072 var nThs = nThead.length===0 ? null : _fnGetUniqueThs( nThead[0] ); | |
5073 var bUseCols = typeof oInit.aoColumns != 'undefined'; | |
5074 | |
5075 for ( i=0, iLen=bUseCols ? oInit.aoColumns.length : nThs.length ; i<iLen ; i++ ) | |
5076 { | |
5077 var oCol = bUseCols ? oInit.aoColumns[i] : null; | |
5078 var nTh = nThs ? nThs[i] : null; | |
5079 | |
5080 /* Check if we have column visibilty state to restore, and also that the length of the | |
5081 * state saved columns matches the currently know number of columns | |
5082 */ | |
5083 if ( typeof oInit.saved_aoColumns != 'undefined' && oInit.saved_aoColumns.length == iLen ) | |
5084 { | |
5085 if ( oCol === null ) | |
5086 { | |
5087 oCol = {}; | |
5088 } | |
5089 oCol.bVisible = oInit.saved_aoColumns[i].bVisible; | |
5090 } | |
5091 | |
5092 _fnAddColumn( oSettings, oCol, nTh ); | |
5093 } | |
5094 | |
5095 /* Check the aaSorting array */ | |
5096 for ( i=0, iLen=oSettings.aaSorting.length ; i<iLen ; i++ ) | |
5097 { | |
5098 var oColumn = oSettings.aoColumns[ oSettings.aaSorting[i][0] ]; | |
5099 | |
5100 /* Add a default sorting index */ | |
5101 if ( typeof oSettings.aaSorting[i][2] == 'undefined' ) | |
5102 { | |
5103 oSettings.aaSorting[i][2] = 0; | |
5104 } | |
5105 | |
5106 /* If aaSorting is not defined, then we use the first indicator in asSorting */ | |
5107 if ( typeof oInit.aaSorting == "undefined" && | |
5108 typeof oSettings.saved_aaSorting == "undefined" ) | |
5109 { | |
5110 oSettings.aaSorting[i][1] = oColumn.asSorting[0]; | |
5111 } | |
5112 | |
5113 /* Set the current sorting index based on aoColumns.asSorting */ | |
5114 for ( j=0, jLen=oColumn.asSorting.length ; j<jLen ; j++ ) | |
5115 { | |
5116 if ( oSettings.aaSorting[i][1] == oColumn.asSorting[j] ) | |
5117 { | |
5118 oSettings.aaSorting[i][2] = j; | |
5119 break; | |
5120 } | |
5121 } | |
5122 } | |
5123 | |
5124 /* Sanity check that there is a thead and tfoot. If not let's just create them */ | |
5125 if ( this.getElementsByTagName('thead').length === 0 ) | |
5126 { | |
5127 this.appendChild( document.createElement( 'thead' ) ); | |
5128 } | |
5129 | |
5130 if ( this.getElementsByTagName('tbody').length === 0 ) | |
5131 { | |
5132 this.appendChild( document.createElement( 'tbody' ) ); | |
5133 } | |
5134 | |
5135 /* Check if there is data passing into the constructor */ | |
5136 if ( bUsePassedData ) | |
5137 { | |
5138 for ( i=0 ; i<oInit.aaData.length ; i++ ) | |
5139 { | |
5140 _fnAddData( oSettings, oInit.aaData[ i ] ); | |
5141 } | |
5142 } | |
5143 else | |
5144 { | |
5145 /* Grab the data from the page */ | |
5146 _fnGatherData( oSettings ); | |
5147 } | |
5148 | |
5149 /* Copy the data index array */ | |
5150 oSettings.aiDisplay = oSettings.aiDisplayMaster.slice(); | |
5151 | |
5152 /* Calculate sizes for columns */ | |
5153 if ( oSettings.oFeatures.bAutoWidth ) | |
5154 { | |
5155 _fnCalculateColumnWidths( oSettings ); | |
5156 } | |
5157 | |
5158 /* Initialisation complete - table can be drawn */ | |
5159 oSettings.bInitialised = true; | |
5160 | |
5161 /* Check if we need to initialise the table (it might not have been handed off to the | |
5162 * language processor) | |
5163 */ | |
5164 if ( bInitHandedOff === false ) | |
5165 { | |
5166 _fnInitalise( oSettings ); | |
5167 } | |
5168 }); | |
5169 }; | |
5170 })(jQuery); |