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