source: main/waeup.sirp/trunk/src/waeup/sirp/browser/static/jquery.dataTables.js @ 6010

Last change on this file since 6010 was 6010, checked in by uli, 14 years ago

Add stylesheets and javascripts for lean datatables and the new theme.

File size: 206.3 KB
Line 
1/*
2 * File:        jquery.dataTables.js
3 * Version:     1.7.6
4 * Description: Paginate, search and sort HTML tables
5 * Author:      Allan Jardine (www.sprymedia.co.uk)
6 * Created:     28/3/2008
7 * Language:    Javascript
8 * License:     GPL v2 or BSD 3 point style
9 * Project:     Mtaala
10 * Contact:     allan.jardine@sprymedia.co.uk
11 *
12 * Copyright 2008-2010 Allan Jardine, all rights reserved.
13 *
14 * This source file is free software, under either the GPL v2 license or a
15 * BSD style license, as supplied with this software.
16 *
17 * This source file is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
19 * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
20 *
21 * For details please refer to: http://www.datatables.net
22 */
23
24/*
25 * When considering jsLint, we need to allow eval() as it it is used for reading cookies and
26 * building the dynamic multi-column sort functions.
27 */
28/*jslint evil: true, undef: true, browser: true */
29/*globals $, jQuery,_fnExternApiFunc,_fnInitalise,_fnInitComplete,_fnLanguageProcess,_fnAddColumn,_fnColumnOptions,_fnAddData,_fnGatherData,_fnDrawHead,_fnDraw,_fnReDraw,_fnAjaxUpdate,_fnAjaxUpdateDraw,_fnAddOptionsHtml,_fnFeatureHtmlTable,_fnScrollDraw,_fnAjustColumnSizing,_fnFeatureHtmlFilter,_fnFilterComplete,_fnFilterCustom,_fnFilterColumn,_fnFilter,_fnBuildSearchArray,_fnBuildSearchRow,_fnFilterCreateSearch,_fnDataToSearch,_fnSort,_fnSortAttachListener,_fnSortingClasses,_fnFeatureHtmlPaginate,_fnPageChange,_fnFeatureHtmlInfo,_fnUpdateInfo,_fnFeatureHtmlLength,_fnFeatureHtmlProcessing,_fnProcessingDisplay,_fnVisibleToColumnIndex,_fnColumnIndexToVisible,_fnNodeToDataIndex,_fnVisbleColumns,_fnCalculateEnd,_fnConvertToWidth,_fnCalculateColumnWidths,_fnScrollingWidthAdjust,_fnGetWidestNode,_fnGetMaxLenString,_fnStringToCss,_fnArrayCmp,_fnDetectType,_fnSettingsFromNode,_fnGetDataMaster,_fnGetTrNodes,_fnGetTdNodes,_fnEscapeRegex,_fnDeleteIndex,_fnReOrderIndex,_fnColumnOrdering,_fnLog,_fnClearTable,_fnSaveState,_fnLoadState,_fnCreateCookie,_fnReadCookie,_fnGetUniqueThs,_fnScrollBarWidth,_fnApplyToChildren,_fnMap*/
30
31(function($, window, document) {
32        /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
33         * Section - DataTables variables
34         * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
35       
36        /*
37         * Variable: dataTableSettings
38         * Purpose:  Store the settings for each dataTables instance
39         * Scope:    jQuery.fn
40         */
41        $.fn.dataTableSettings = [];
42        var _aoSettings = $.fn.dataTableSettings; /* Short reference for fast internal lookup */
43       
44        /*
45         * Variable: dataTableExt
46         * Purpose:  Container for customisable parts of DataTables
47         * Scope:    jQuery.fn
48         */
49        $.fn.dataTableExt = {};
50        var _oExt = $.fn.dataTableExt;
51       
52       
53        /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
54         * Section - DataTables extensible objects
55         *
56         * The _oExt object is used to provide an area where user dfined plugins can be
57         * added to DataTables. The following properties of the object are used:
58         *   oApi - Plug-in API functions
59         *   aTypes - Auto-detection of types
60         *   oSort - Sorting functions used by DataTables (based on the type)
61         *   oPagination - Pagination functions for different input styles
62         * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
63       
64        /*
65         * Variable: sVersion
66         * Purpose:  Version string for plug-ins to check compatibility
67         * Scope:    jQuery.fn.dataTableExt
68         * Notes:    Allowed format is a.b.c.d.e where:
69         *   a:int, b:int, c:int, d:string(dev|beta), e:int. d and e are optional
70         */
71        _oExt.sVersion = "1.7.6";
72       
73        /*
74         * Variable: sErrMode
75         * Purpose:  How should DataTables report an error. Can take the value 'alert' or 'throw'
76         * Scope:    jQuery.fn.dataTableExt
77         */
78        _oExt.sErrMode = "alert";
79       
80        /*
81         * Variable: iApiIndex
82         * Purpose:  Index for what 'this' index API functions should use
83         * Scope:    jQuery.fn.dataTableExt
84         */
85        _oExt.iApiIndex = 0;
86       
87        /*
88         * Variable: oApi
89         * Purpose:  Container for plugin API functions
90         * Scope:    jQuery.fn.dataTableExt
91         */
92        _oExt.oApi = { };
93       
94        /*
95         * Variable: aFiltering
96         * Purpose:  Container for plugin filtering functions
97         * Scope:    jQuery.fn.dataTableExt
98         */
99        _oExt.afnFiltering = [ ];
100       
101        /*
102         * Variable: aoFeatures
103         * Purpose:  Container for plugin function functions
104         * Scope:    jQuery.fn.dataTableExt
105         * Notes:    Array of objects with the following parameters:
106         *   fnInit: Function for initialisation of Feature. Takes oSettings and returns node
107         *   cFeature: Character that will be matched in sDom - case sensitive
108         *   sFeature: Feature name - just for completeness :-)
109         */
110        _oExt.aoFeatures = [ ];
111       
112        /*
113         * Variable: ofnSearch
114         * Purpose:  Container for custom filtering functions
115         * Scope:    jQuery.fn.dataTableExt
116         * Notes:    This is an object (the name should match the type) for custom filtering function,
117         *   which can be used for live DOM checking or formatted text filtering
118         */
119        _oExt.ofnSearch = { };
120       
121        /*
122         * Variable: afnSortData
123         * Purpose:  Container for custom sorting data source functions
124         * Scope:    jQuery.fn.dataTableExt
125         * Notes:    Array (associative) of functions which is run prior to a column of this
126         *   'SortDataType' being sorted upon.
127         *   Function input parameters:
128         *     object:oSettings-  DataTables settings object
129         *     int:iColumn - Target column number
130         *   Return value: Array of data which exactly matched the full data set size for the column to
131         *     be sorted upon
132         */
133        _oExt.afnSortData = [ ];
134       
135        /*
136         * Variable: oStdClasses
137         * Purpose:  Storage for the various classes that DataTables uses
138         * Scope:    jQuery.fn.dataTableExt
139         */
140        _oExt.oStdClasses = {
141                /* Two buttons buttons */
142                "sPagePrevEnabled": "paginate_enabled_previous",
143                "sPagePrevDisabled": "paginate_disabled_previous",
144                "sPageNextEnabled": "paginate_enabled_next",
145                "sPageNextDisabled": "paginate_disabled_next",
146                "sPageJUINext": "",
147                "sPageJUIPrev": "",
148               
149                /* Full numbers paging buttons */
150                "sPageButton": "paginate_button",
151                "sPageButtonActive": "paginate_active",
152                "sPageButtonStaticDisabled": "paginate_button",
153                "sPageFirst": "first",
154                "sPagePrevious": "previous",
155                "sPageNext": "next",
156                "sPageLast": "last",
157               
158                /* Stripping classes */
159                "sStripOdd": "odd",
160                "sStripEven": "even",
161               
162                /* Empty row */
163                "sRowEmpty": "dataTables_empty",
164               
165                /* Features */
166                "sWrapper": "dataTables_wrapper",
167                "sFilter": "dataTables_filter",
168                "sInfo": "dataTables_info",
169                "sPaging": "dataTables_paginate paging_", /* Note that the type is postfixed */
170                "sLength": "dataTables_length",
171                "sProcessing": "dataTables_processing",
172               
173                /* Sorting */
174                "sSortAsc": "sorting_asc",
175                "sSortDesc": "sorting_desc",
176                "sSortable": "sorting", /* Sortable in both directions */
177                "sSortableAsc": "sorting_asc_disabled",
178                "sSortableDesc": "sorting_desc_disabled",
179                "sSortableNone": "sorting_disabled",
180                "sSortColumn": "sorting_", /* Note that an int is postfixed for the sorting order */
181                "sSortJUIAsc": "",
182                "sSortJUIDesc": "",
183                "sSortJUI": "",
184                "sSortJUIAscAllowed": "",
185                "sSortJUIDescAllowed": "",
186                "sSortJUIWrapper": "",
187               
188                /* Scrolling */
189                "sScrollWrapper": "dataTables_scroll",
190                "sScrollHead": "dataTables_scrollHead",
191                "sScrollHeadInner": "dataTables_scrollHeadInner",
192                "sScrollBody": "dataTables_scrollBody",
193                "sScrollFoot": "dataTables_scrollFoot",
194                "sScrollFootInner": "dataTables_scrollFootInner",
195               
196                /* Misc */
197                "sFooterTH": ""
198        };
199       
200        /*
201         * Variable: oJUIClasses
202         * Purpose:  Storage for the various classes that DataTables uses - jQuery UI suitable
203         * Scope:    jQuery.fn.dataTableExt
204         */
205        _oExt.oJUIClasses = {
206                /* Two buttons buttons */
207                "sPagePrevEnabled": "fg-button ui-button ui-state-default ui-corner-left",
208                "sPagePrevDisabled": "fg-button ui-button ui-state-default ui-corner-left ui-state-disabled",
209                "sPageNextEnabled": "fg-button ui-button ui-state-default ui-corner-right",
210                "sPageNextDisabled": "fg-button ui-button ui-state-default ui-corner-right ui-state-disabled",
211                "sPageJUINext": "ui-icon ui-icon-circle-arrow-e",
212                "sPageJUIPrev": "ui-icon ui-icon-circle-arrow-w",
213               
214                /* Full numbers paging buttons */
215                "sPageButton": "fg-button ui-button ui-state-default",
216                "sPageButtonActive": "fg-button ui-button ui-state-default ui-state-disabled",
217                "sPageButtonStaticDisabled": "fg-button ui-button ui-state-default ui-state-disabled",
218                "sPageFirst": "first ui-corner-tl ui-corner-bl",
219                "sPagePrevious": "previous",
220                "sPageNext": "next",
221                "sPageLast": "last ui-corner-tr ui-corner-br",
222               
223                /* Stripping classes */
224                "sStripOdd": "odd",
225                "sStripEven": "even",
226               
227                /* Empty row */
228                "sRowEmpty": "dataTables_empty",
229               
230                /* Features */
231                "sWrapper": "dataTables_wrapper",
232                "sFilter": "dataTables_filter",
233                "sInfo": "dataTables_info",
234                "sPaging": "dataTables_paginate fg-buttonset ui-buttonset fg-buttonset-multi "+
235                        "ui-buttonset-multi paging_", /* Note that the type is postfixed */
236                "sLength": "dataTables_length",
237                "sProcessing": "dataTables_processing",
238               
239                /* Sorting */
240                "sSortAsc": "ui-state-default",
241                "sSortDesc": "ui-state-default",
242                "sSortable": "ui-state-default",
243                "sSortableAsc": "ui-state-default",
244                "sSortableDesc": "ui-state-default",
245                "sSortableNone": "ui-state-default",
246                "sSortColumn": "sorting_", /* Note that an int is postfixed for the sorting order */
247                "sSortJUIAsc": "css_right ui-icon ui-icon-triangle-1-n",
248                "sSortJUIDesc": "css_right ui-icon ui-icon-triangle-1-s",
249                "sSortJUI": "css_right ui-icon ui-icon-carat-2-n-s",
250                "sSortJUIAscAllowed": "css_right ui-icon ui-icon-carat-1-n",
251                "sSortJUIDescAllowed": "css_right ui-icon ui-icon-carat-1-s",
252                "sSortJUIWrapper": "DataTables_sort_wrapper",
253               
254                /* Scrolling */
255                "sScrollWrapper": "dataTables_scroll",
256                "sScrollHead": "dataTables_scrollHead ui-state-default",
257                "sScrollHeadInner": "dataTables_scrollHeadInner",
258                "sScrollBody": "dataTables_scrollBody",
259                "sScrollFoot": "dataTables_scrollFoot ui-state-default",
260                "sScrollFootInner": "dataTables_scrollFootInner",
261               
262                /* Misc */
263                "sFooterTH": "ui-state-default"
264        };
265       
266        /*
267         * Variable: oPagination
268         * Purpose:  Container for the various type of pagination that dataTables supports
269         * Scope:    jQuery.fn.dataTableExt
270         */
271        _oExt.oPagination = {
272                /*
273                 * Variable: two_button
274                 * Purpose:  Standard two button (forward/back) pagination
275                 * Scope:    jQuery.fn.dataTableExt.oPagination
276                 */
277                "two_button": {
278                        /*
279                         * Function: oPagination.two_button.fnInit
280                         * Purpose:  Initalise dom elements required for pagination with forward/back buttons only
281                         * Returns:  -
282                         * Inputs:   object:oSettings - dataTables settings object
283             *           node:nPaging - the DIV which contains this pagination control
284                         *           function:fnCallbackDraw - draw function which must be called on update
285                         */
286                        "fnInit": function ( oSettings, nPaging, fnCallbackDraw )
287                        {
288                                var nPrevious, nNext, nPreviousInner, nNextInner;
289                               
290                                /* Store the next and previous elements in the oSettings object as they can be very
291                                 * usful for automation - particularly testing
292                                 */
293                                if ( !oSettings.bJUI )
294                                {
295                                        nPrevious = document.createElement( 'div' );
296                                        nNext = document.createElement( 'div' );
297                                }
298                                else
299                                {
300                                        nPrevious = document.createElement( 'a' );
301                                        nNext = document.createElement( 'a' );
302                                       
303                                        nNextInner = document.createElement('span');
304                                        nNextInner.className = oSettings.oClasses.sPageJUINext;
305                                        nNext.appendChild( nNextInner );
306                                       
307                                        nPreviousInner = document.createElement('span');
308                                        nPreviousInner.className = oSettings.oClasses.sPageJUIPrev;
309                                        nPrevious.appendChild( nPreviousInner );
310                                }
311                               
312                                nPrevious.className = oSettings.oClasses.sPagePrevDisabled;
313                                nNext.className = oSettings.oClasses.sPageNextDisabled;
314                               
315                                nPrevious.title = oSettings.oLanguage.oPaginate.sPrevious;
316                                nNext.title = oSettings.oLanguage.oPaginate.sNext;
317                               
318                                nPaging.appendChild( nPrevious );
319                                nPaging.appendChild( nNext );
320                               
321                                $(nPrevious).bind( 'click.DT', function() {
322                                        if ( oSettings.oApi._fnPageChange( oSettings, "previous" ) )
323                                        {
324                                                /* Only draw when the page has actually changed */
325                                                fnCallbackDraw( oSettings );
326                                        }
327                                } );
328                               
329                                $(nNext).bind( 'click.DT', function() {
330                                        if ( oSettings.oApi._fnPageChange( oSettings, "next" ) )
331                                        {
332                                                fnCallbackDraw( oSettings );
333                                        }
334                                } );
335                               
336                                /* Take the brutal approach to cancelling text selection */
337                                $(nPrevious).bind( 'selectstart.DT', function () { return false; } );
338                                $(nNext).bind( 'selectstart.DT', function () { return false; } );
339                               
340                                /* ID the first elements only */
341                                if ( oSettings.sTableId !== '' && typeof oSettings.aanFeatures.p == "undefined" )
342                                {
343                                        nPaging.setAttribute( 'id', oSettings.sTableId+'_paginate' );
344                                        nPrevious.setAttribute( 'id', oSettings.sTableId+'_previous' );
345                                        nNext.setAttribute( 'id', oSettings.sTableId+'_next' );
346                                }
347                        },
348                       
349                        /*
350                         * Function: oPagination.two_button.fnUpdate
351                         * Purpose:  Update the two button pagination at the end of the draw
352                         * Returns:  -
353                         * Inputs:   object:oSettings - dataTables settings object
354                         *           function:fnCallbackDraw - draw function to call on page change
355                         */
356                        "fnUpdate": function ( oSettings, fnCallbackDraw )
357                        {
358                                if ( !oSettings.aanFeatures.p )
359                                {
360                                        return;
361                                }
362                               
363                                /* Loop over each instance of the pager */
364                                var an = oSettings.aanFeatures.p;
365                                for ( var i=0, iLen=an.length ; i<iLen ; i++ )
366                                {
367                                        if ( an[i].childNodes.length !== 0 )
368                                        {
369                                                an[i].childNodes[0].className =
370                                                        ( oSettings._iDisplayStart === 0 ) ?
371                                                        oSettings.oClasses.sPagePrevDisabled : oSettings.oClasses.sPagePrevEnabled;
372                                               
373                                                an[i].childNodes[1].className =
374                                                        ( oSettings.fnDisplayEnd() == oSettings.fnRecordsDisplay() ) ?
375                                                        oSettings.oClasses.sPageNextDisabled : oSettings.oClasses.sPageNextEnabled;
376                                        }
377                                }
378                        }
379                },
380               
381               
382                /*
383                 * Variable: iFullNumbersShowPages
384                 * Purpose:  Change the number of pages which can be seen
385                 * Scope:    jQuery.fn.dataTableExt.oPagination
386                 */
387                "iFullNumbersShowPages": 5,
388               
389                /*
390                 * Variable: full_numbers
391                 * Purpose:  Full numbers pagination
392                 * Scope:    jQuery.fn.dataTableExt.oPagination
393                 */
394                "full_numbers": {
395                        /*
396                         * Function: oPagination.full_numbers.fnInit
397                         * Purpose:  Initalise dom elements required for pagination with a list of the pages
398                         * Returns:  -
399                         * Inputs:   object:oSettings - dataTables settings object
400             *           node:nPaging - the DIV which contains this pagination control
401                         *           function:fnCallbackDraw - draw function which must be called on update
402                         */
403                        "fnInit": function ( oSettings, nPaging, fnCallbackDraw )
404                        {
405                                var nFirst = document.createElement( 'span' );
406                                var nPrevious = document.createElement( 'span' );
407                                var nList = document.createElement( 'span' );
408                                var nNext = document.createElement( 'span' );
409                                var nLast = document.createElement( 'span' );
410                               
411                                nFirst.innerHTML = oSettings.oLanguage.oPaginate.sFirst;
412                                nPrevious.innerHTML = oSettings.oLanguage.oPaginate.sPrevious;
413                                nNext.innerHTML = oSettings.oLanguage.oPaginate.sNext;
414                                nLast.innerHTML = oSettings.oLanguage.oPaginate.sLast;
415                               
416                                var oClasses = oSettings.oClasses;
417                                nFirst.className = oClasses.sPageButton+" "+oClasses.sPageFirst;
418                                nPrevious.className = oClasses.sPageButton+" "+oClasses.sPagePrevious;
419                                nNext.className= oClasses.sPageButton+" "+oClasses.sPageNext;
420                                nLast.className = oClasses.sPageButton+" "+oClasses.sPageLast;
421                               
422                                nPaging.appendChild( nFirst );
423                                nPaging.appendChild( nPrevious );
424                                nPaging.appendChild( nList );
425                                nPaging.appendChild( nNext );
426                                nPaging.appendChild( nLast );
427                               
428                                $(nFirst).bind( 'click.DT', function () {
429                                        if ( oSettings.oApi._fnPageChange( oSettings, "first" ) )
430                                        {
431                                                fnCallbackDraw( oSettings );
432                                        }
433                                } );
434                               
435                                $(nPrevious).bind( 'click.DT', function() {
436                                        if ( oSettings.oApi._fnPageChange( oSettings, "previous" ) )
437                                        {
438                                                fnCallbackDraw( oSettings );
439                                        }
440                                } );
441                               
442                                $(nNext).bind( 'click.DT', function() {
443                                        if ( oSettings.oApi._fnPageChange( oSettings, "next" ) )
444                                        {
445                                                fnCallbackDraw( oSettings );
446                                        }
447                                } );
448                               
449                                $(nLast).bind( 'click.DT', function() {
450                                        if ( oSettings.oApi._fnPageChange( oSettings, "last" ) )
451                                        {
452                                                fnCallbackDraw( oSettings );
453                                        }
454                                } );
455                               
456                                /* Take the brutal approach to cancelling text selection */
457                                $('span', nPaging)
458                                        .bind( 'mousedown.DT', function () { return false; } )
459                                        .bind( 'selectstart.DT', function () { return false; } );
460                               
461                                /* ID the first elements only */
462                                if ( oSettings.sTableId !== '' && typeof oSettings.aanFeatures.p == "undefined" )
463                                {
464                                        nPaging.setAttribute( 'id', oSettings.sTableId+'_paginate' );
465                                        nFirst.setAttribute( 'id', oSettings.sTableId+'_first' );
466                                        nPrevious.setAttribute( 'id', oSettings.sTableId+'_previous' );
467                                        nNext.setAttribute( 'id', oSettings.sTableId+'_next' );
468                                        nLast.setAttribute( 'id', oSettings.sTableId+'_last' );
469                                }
470                        },
471                       
472                        /*
473                         * Function: oPagination.full_numbers.fnUpdate
474                         * Purpose:  Update the list of page buttons shows
475                         * Returns:  -
476                         * Inputs:   object:oSettings - dataTables settings object
477                         *           function:fnCallbackDraw - draw function to call on page change
478                         */
479                        "fnUpdate": function ( oSettings, fnCallbackDraw )
480                        {
481                                if ( !oSettings.aanFeatures.p )
482                                {
483                                        return;
484                                }
485                               
486                                var iPageCount = _oExt.oPagination.iFullNumbersShowPages;
487                                var iPageCountHalf = Math.floor(iPageCount / 2);
488                                var iPages = Math.ceil((oSettings.fnRecordsDisplay()) / oSettings._iDisplayLength);
489                                var iCurrentPage = Math.ceil(oSettings._iDisplayStart / oSettings._iDisplayLength) + 1;
490                                var sList = "";
491                                var iStartButton, iEndButton, i, iLen;
492                                var oClasses = oSettings.oClasses;
493                               
494                                /* Pages calculation */
495                                if (iPages < iPageCount)
496                                {
497                                        iStartButton = 1;
498                                        iEndButton = iPages;
499                                }
500                                else
501                                {
502                                        if (iCurrentPage <= iPageCountHalf)
503                                        {
504                                                iStartButton = 1;
505                                                iEndButton = iPageCount;
506                                        }
507                                        else
508                                        {
509                                                if (iCurrentPage >= (iPages - iPageCountHalf))
510                                                {
511                                                        iStartButton = iPages - iPageCount + 1;
512                                                        iEndButton = iPages;
513                                                }
514                                                else
515                                                {
516                                                        iStartButton = iCurrentPage - Math.ceil(iPageCount / 2) + 1;
517                                                        iEndButton = iStartButton + iPageCount - 1;
518                                                }
519                                        }
520                                }
521                               
522                                /* Build the dynamic list */
523                                for ( i=iStartButton ; i<=iEndButton ; i++ )
524                                {
525                                        if ( iCurrentPage != i )
526                                        {
527                                                sList += '<span class="'+oClasses.sPageButton+'">'+i+'</span>';
528                                        }
529                                        else
530                                        {
531                                                sList += '<span class="'+oClasses.sPageButtonActive+'">'+i+'</span>';
532                                        }
533                                }
534                               
535                                /* Loop over each instance of the pager */
536                                var an = oSettings.aanFeatures.p;
537                                var anButtons, anStatic, nPaginateList;
538                                var fnClick = function() {
539                                        /* Use the information in the element to jump to the required page */
540                                        var iTarget = (this.innerHTML * 1) - 1;
541                                        oSettings._iDisplayStart = iTarget * oSettings._iDisplayLength;
542                                        fnCallbackDraw( oSettings );
543                                        return false;
544                                };
545                                var fnFalse = function () { return false; };
546                               
547                                for ( i=0, iLen=an.length ; i<iLen ; i++ )
548                                {
549                                        if ( an[i].childNodes.length === 0 )
550                                        {
551                                                continue;
552                                        }
553                                       
554                                        /* Build up the dynamic list forst - html and listeners */
555                                        var qjPaginateList = $('span:eq(2)', an[i]);
556                                        qjPaginateList.html( sList );
557                                        $('span', qjPaginateList).bind( 'click.DT', fnClick ).bind( 'mousedown.DT', fnFalse )
558                                                .bind( 'selectstart.DT', fnFalse );
559                                       
560                                        /* Update the 'premanent botton's classes */
561                                        anButtons = an[i].getElementsByTagName('span');
562                                        anStatic = [
563                                                anButtons[0], anButtons[1],
564                                                anButtons[anButtons.length-2], anButtons[anButtons.length-1]
565                                        ];
566                                        $(anStatic).removeClass( oClasses.sPageButton+" "+oClasses.sPageButtonActive+" "+oClasses.sPageButtonStaticDisabled );
567                                        if ( iCurrentPage == 1 )
568                                        {
569                                                anStatic[0].className += " "+oClasses.sPageButtonStaticDisabled;
570                                                anStatic[1].className += " "+oClasses.sPageButtonStaticDisabled;
571                                        }
572                                        else
573                                        {
574                                                anStatic[0].className += " "+oClasses.sPageButton;
575                                                anStatic[1].className += " "+oClasses.sPageButton;
576                                        }
577                                       
578                                        if ( iPages === 0 || iCurrentPage == iPages || oSettings._iDisplayLength == -1 )
579                                        {
580                                                anStatic[2].className += " "+oClasses.sPageButtonStaticDisabled;
581                                                anStatic[3].className += " "+oClasses.sPageButtonStaticDisabled;
582                                        }
583                                        else
584                                        {
585                                                anStatic[2].className += " "+oClasses.sPageButton;
586                                                anStatic[3].className += " "+oClasses.sPageButton;
587                                        }
588                                }
589                        }
590                }
591        };
592       
593        /*
594         * Variable: oSort
595         * Purpose:  Wrapper for the sorting functions that can be used in DataTables
596         * Scope:    jQuery.fn.dataTableExt
597         * Notes:    The functions provided in this object are basically standard javascript sort
598         *   functions - they expect two inputs which they then compare and then return a priority
599         *   result. For each sort method added, two functions need to be defined, an ascending sort and
600         *   a descending sort.
601         */
602        _oExt.oSort = {
603                /*
604                 * text sorting
605                 */
606                "string-asc": function ( a, b )
607                {
608                        var x = a.toLowerCase();
609                        var y = b.toLowerCase();
610                        return ((x < y) ? -1 : ((x > y) ? 1 : 0));
611                },
612               
613                "string-desc": function ( a, b )
614                {
615                        var x = a.toLowerCase();
616                        var y = b.toLowerCase();
617                        return ((x < y) ? 1 : ((x > y) ? -1 : 0));
618                },
619               
620               
621                /*
622                 * html sorting (ignore html tags)
623                 */
624                "html-asc": function ( a, b )
625                {
626                        var x = a.replace( /<.*?>/g, "" ).toLowerCase();
627                        var y = b.replace( /<.*?>/g, "" ).toLowerCase();
628                        return ((x < y) ? -1 : ((x > y) ? 1 : 0));
629                },
630               
631                "html-desc": function ( a, b )
632                {
633                        var x = a.replace( /<.*?>/g, "" ).toLowerCase();
634                        var y = b.replace( /<.*?>/g, "" ).toLowerCase();
635                        return ((x < y) ? 1 : ((x > y) ? -1 : 0));
636                },
637               
638               
639                /*
640                 * date sorting
641                 */
642                "date-asc": function ( a, b )
643                {
644                        var x = Date.parse( a );
645                        var y = Date.parse( b );
646                       
647                        if ( isNaN(x) || x==="" )
648                        {
649                x = Date.parse( "01/01/1970 00:00:00" );
650                        }
651                        if ( isNaN(y) || y==="" )
652                        {
653                                y =     Date.parse( "01/01/1970 00:00:00" );
654                        }
655                       
656                        return x - y;
657                },
658               
659                "date-desc": function ( a, b )
660                {
661                        var x = Date.parse( a );
662                        var y = Date.parse( b );
663                       
664                        if ( isNaN(x) || x==="" )
665                        {
666                x = Date.parse( "01/01/1970 00:00:00" );
667                        }
668                        if ( isNaN(y) || y==="" )
669                        {
670                                y =     Date.parse( "01/01/1970 00:00:00" );
671                        }
672                       
673                        return y - x;
674                },
675               
676               
677                /*
678                 * numerical sorting
679                 */
680                "numeric-asc": function ( a, b )
681                {
682                        var x = (a=="-" || a==="") ? 0 : a*1;
683                        var y = (b=="-" || b==="") ? 0 : b*1;
684                        return x - y;
685                },
686               
687                "numeric-desc": function ( a, b )
688                {
689                        var x = (a=="-" || a==="") ? 0 : a*1;
690                        var y = (b=="-" || b==="") ? 0 : b*1;
691                        return y - x;
692                }
693        };
694       
695       
696        /*
697         * Variable: aTypes
698         * Purpose:  Container for the various type of type detection that dataTables supports
699         * Scope:    jQuery.fn.dataTableExt
700         * Notes:    The functions in this array are expected to parse a string to see if it is a data
701         *   type that it recognises. If so then the function should return the name of the type (a
702         *   corresponding sort function should be defined!), if the type is not recognised then the
703         *   function should return null such that the parser and move on to check the next type.
704         *   Note that ordering is important in this array - the functions are processed linearly,
705         *   starting at index 0.
706         *   Note that the input for these functions is always a string! It cannot be any other data
707         *   type
708         */
709        _oExt.aTypes = [
710                /*
711                 * Function: -
712                 * Purpose:  Check to see if a string is numeric
713                 * Returns:  string:'numeric' or null
714                 * Inputs:   string:sText - string to check
715                 */
716                function ( sData )
717                {
718                        /* Allow zero length strings as a number */
719                        if ( sData.length === 0 )
720                        {
721                                return 'numeric';
722                        }
723                       
724                        var sValidFirstChars = "0123456789-";
725                        var sValidChars = "0123456789.";
726                        var Char;
727                        var bDecimal = false;
728                       
729                        /* Check for a valid first char (no period and allow negatives) */
730                        Char = sData.charAt(0);
731                        if (sValidFirstChars.indexOf(Char) == -1)
732                        {
733                                return null;
734                        }
735                       
736                        /* Check all the other characters are valid */
737                        for ( var i=1 ; i<sData.length ; i++ )
738                        {
739                                Char = sData.charAt(i);
740                                if (sValidChars.indexOf(Char) == -1)
741                                {
742                                        return null;
743                                }
744                               
745                                /* Only allowed one decimal place... */
746                                if ( Char == "." )
747                                {
748                                        if ( bDecimal )
749                                        {
750                                                return null;
751                                        }
752                                        bDecimal = true;
753                                }
754                        }
755                       
756                        return 'numeric';
757                },
758               
759                /*
760                 * Function: -
761                 * Purpose:  Check to see if a string is actually a formatted date
762                 * Returns:  string:'date' or null
763                 * Inputs:   string:sText - string to check
764                 */
765                function ( sData )
766                {
767                        var iParse = Date.parse(sData);
768                        if ( (iParse !== null && !isNaN(iParse)) || sData.length === 0 )
769                        {
770                                return 'date';
771                        }
772                        return null;
773                },
774               
775                /*
776                 * Function: -
777                 * Purpose:  Check to see if a string should be treated as an HTML string
778                 * Returns:  string:'html' or null
779                 * Inputs:   string:sText - string to check
780                 */
781                function ( sData )
782                {
783                        if ( sData.indexOf('<') != -1 && sData.indexOf('>') != -1 )
784                        {
785                                return 'html';
786                        }
787                        return null;
788                }
789        ];
790       
791        /*
792         * Function: fnVersionCheck
793         * Purpose:  Check a version string against this version of DataTables. Useful for plug-ins
794         * Returns:  bool:true -this version of DataTables is greater or equal to the required version
795         *                false -this version of DataTales is not suitable
796         * Inputs:   string:sVersion - the version to check against. May be in the following formats:
797         *             "a", "a.b" or "a.b.c"
798         * Notes:    This function will only check the first three parts of a version string. It is
799         *   assumed that beta and dev versions will meet the requirements. This might change in future
800         */
801        _oExt.fnVersionCheck = function( sVersion )
802        {
803                /* This is cheap, but very effective */
804                var fnZPad = function (Zpad, count)
805                {
806                        while(Zpad.length < count) {
807                                Zpad += '0';
808                        }
809                        return Zpad;
810                };
811                var aThis = _oExt.sVersion.split('.');
812                var aThat = sVersion.split('.');
813                var sThis = '', sThat = '';
814               
815                for ( var i=0, iLen=aThat.length ; i<iLen ; i++ )
816                {
817                        sThis += fnZPad( aThis[i], 3 );
818                        sThat += fnZPad( aThat[i], 3 );
819                }
820               
821                return parseInt(sThis, 10) >= parseInt(sThat, 10);
822        };
823       
824        /*
825         * Variable: _oExternConfig
826         * Purpose:  Store information for DataTables to access globally about other instances
827         * Scope:    jQuery.fn.dataTableExt
828         */
829        _oExt._oExternConfig = {
830                /* int:iNextUnique - next unique number for an instance */
831                "iNextUnique": 0
832        };
833       
834       
835        /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
836         * Section - DataTables prototype
837         * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
838       
839        /*
840         * Function: dataTable
841         * Purpose:  DataTables information
842         * Returns:  -
843         * Inputs:   object:oInit - initalisation options for the table
844         */
845        $.fn.dataTable = function( oInit )
846        {
847                /*
848                 * Function: classSettings
849                 * Purpose:  Settings container function for all 'class' properties which are required
850                 *   by dataTables
851                 * Returns:  -
852                 * Inputs:   -
853                 */
854                function classSettings ()
855                {
856                        this.fnRecordsTotal = function ()
857                        {
858                                if ( this.oFeatures.bServerSide ) {
859                                        return parseInt(this._iRecordsTotal, 10);
860                                } else {
861                                        return this.aiDisplayMaster.length;
862                                }
863                        };
864                       
865                        this.fnRecordsDisplay = function ()
866                        {
867                                if ( this.oFeatures.bServerSide ) {
868                                        return parseInt(this._iRecordsDisplay, 10);
869                                } else {
870                                        return this.aiDisplay.length;
871                                }
872                        };
873                       
874                        this.fnDisplayEnd = function ()
875                        {
876                                if ( this.oFeatures.bServerSide ) {
877                                        if ( this.oFeatures.bPaginate === false || this._iDisplayLength == -1 ) {
878                                                return this._iDisplayStart+this.aiDisplay.length;
879                                        } else {
880                                                return Math.min( this._iDisplayStart+this._iDisplayLength,
881                                                        this._iRecordsDisplay );
882                                        }
883                                } else {
884                                        return this._iDisplayEnd;
885                                }
886                        };
887                       
888                        /*
889                         * Variable: oInstance
890                         * Purpose:  The DataTables object for this table
891                         * Scope:    jQuery.dataTable.classSettings
892                         */
893                        this.oInstance = null;
894                       
895                        /*
896                         * Variable: sInstance
897                         * Purpose:  Unique idendifier for each instance of the DataTables object
898                         * Scope:    jQuery.dataTable.classSettings
899                         */
900                        this.sInstance = null;
901                       
902                        /*
903                         * Variable: oFeatures
904                         * Purpose:  Indicate the enablement of key dataTable features
905                         * Scope:    jQuery.dataTable.classSettings
906                         */
907                        this.oFeatures = {
908                                "bPaginate": true,
909                                "bLengthChange": true,
910                                "bFilter": true,
911                                "bSort": true,
912                                "bInfo": true,
913                                "bAutoWidth": true,
914                                "bProcessing": false,
915                                "bSortClasses": true,
916                                "bStateSave": false,
917                                "bServerSide": false
918                        };
919                       
920                        /*
921                         * Variable: oScroll
922                         * Purpose:  Container for scrolling options
923                         * Scope:    jQuery.dataTable.classSettings
924                         */
925                        this.oScroll = {
926                                "sX": "",
927                                "sXInner": "",
928                                "sY": "",
929                                "bCollapse": false,
930                                "bInfinite": false,
931                                "iLoadGap": 100,
932                                "iBarWidth": 0,
933                                "bAutoCss": true
934                        };
935                       
936                        /*
937                         * Variable: aanFeatures
938                         * Purpose:  Array referencing the nodes which are used for the features
939                         * Scope:    jQuery.dataTable.classSettings
940                         * Notes:    The parameters of this object match what is allowed by sDom - i.e.
941                         *   'l' - Length changing
942                         *   'f' - Filtering input
943                         *   't' - The table!
944                         *   'i' - Information
945                         *   'p' - Pagination
946                         *   'r' - pRocessing
947                         */
948                        this.aanFeatures = [];
949                       
950                        /*
951                         * Variable: oLanguage
952                         * Purpose:  Store the language strings used by dataTables
953                         * Scope:    jQuery.dataTable.classSettings
954                         * Notes:    The words in the format _VAR_ are variables which are dynamically replaced
955                         *   by javascript
956                         */
957                        this.oLanguage = {
958                                "sProcessing": "Processing...",
959                                "sLengthMenu": "Show _MENU_ entries",
960                                "sZeroRecords": "No matching records found",
961                                "sEmptyTable": "No data available in table",
962                                "sInfo": "Showing _START_ to _END_ of _TOTAL_ entries",
963                                "sInfoEmpty": "Showing 0 to 0 of 0 entries",
964                                "sInfoFiltered": "(filtered from _MAX_ total entries)",
965                                "sInfoPostFix": "",
966                                "sSearch": "Search:",
967                                "sUrl": "",
968                                "oPaginate": {
969                                        "sFirst":    "First",
970                                        "sPrevious": "Previous",
971                                        "sNext":     "Next",
972                                        "sLast":     "Last"
973                                },
974                                "fnInfoCallback": null
975                        };
976                       
977                        /*
978                         * Variable: aoData
979                         * Purpose:  Store data information
980                         * Scope:    jQuery.dataTable.classSettings
981                         * Notes:    This is an array of objects with the following parameters:
982                         *   int: _iId - internal id for tracking
983                         *   array: _aData - internal data - used for sorting / filtering etc
984                         *   node: nTr - display node
985                         *   array node: _anHidden - hidden TD nodes
986                         *   string: _sRowStripe
987                         */
988                        this.aoData = [];
989                       
990                        /*
991                         * Variable: aiDisplay
992                         * Purpose:  Array of indexes which are in the current display (after filtering etc)
993                         * Scope:    jQuery.dataTable.classSettings
994                         */
995                        this.aiDisplay = [];
996                       
997                        /*
998                         * Variable: aiDisplayMaster
999                         * Purpose:  Array of indexes for display - no filtering
1000                         * Scope:    jQuery.dataTable.classSettings
1001                         */
1002                        this.aiDisplayMaster = [];
1003                                                       
1004                        /*
1005                         * Variable: aoColumns
1006                         * Purpose:  Store information about each column that is in use
1007                         * Scope:    jQuery.dataTable.classSettings
1008                         */
1009                        this.aoColumns = [];
1010                       
1011                        /*
1012                         * Variable: iNextId
1013                         * Purpose:  Store the next unique id to be used for a new row
1014                         * Scope:    jQuery.dataTable.classSettings
1015                         */
1016                        this.iNextId = 0;
1017                       
1018                        /*
1019                         * Variable: asDataSearch
1020                         * Purpose:  Search data array for regular expression searching
1021                         * Scope:    jQuery.dataTable.classSettings
1022                         */
1023                        this.asDataSearch = [];
1024                       
1025                        /*
1026                         * Variable: oPreviousSearch
1027                         * Purpose:  Store the previous search incase we want to force a re-search
1028                         *   or compare the old search to a new one
1029                         * Scope:    jQuery.dataTable.classSettings
1030                         */
1031                        this.oPreviousSearch = {
1032                                "sSearch": "",
1033                                "bRegex": false,
1034                                "bSmart": true
1035                        };
1036                       
1037                        /*
1038                         * Variable: aoPreSearchCols
1039                         * Purpose:  Store the previous search for each column
1040                         * Scope:    jQuery.dataTable.classSettings
1041                         */
1042                        this.aoPreSearchCols = [];
1043                       
1044                        /*
1045                         * Variable: aaSorting
1046                         * Purpose:  Sorting information
1047                         * Scope:    jQuery.dataTable.classSettings
1048                         * Notes:    Index 0 - column number
1049                         *           Index 1 - current sorting direction
1050                         *           Index 2 - index of asSorting for this column
1051                         */
1052                        this.aaSorting = [ [0, 'asc', 0] ];
1053                       
1054                        /*
1055                         * Variable: aaSortingFixed
1056                         * Purpose:  Sorting information that is always applied
1057                         * Scope:    jQuery.dataTable.classSettings
1058                         */
1059                        this.aaSortingFixed = null;
1060                       
1061                        /*
1062                         * Variable: asStripClasses
1063                         * Purpose:  Classes to use for the striping of a table
1064                         * Scope:    jQuery.dataTable.classSettings
1065                         */
1066                        this.asStripClasses = [];
1067                       
1068                        /*
1069                         * Variable: asDestoryStrips
1070                         * Purpose:  If restoring a table - we should restore it's striping classes as well
1071                         * Scope:    jQuery.dataTable.classSettings
1072                         */
1073                        this.asDestoryStrips = [];
1074                       
1075                        /*
1076                         * Variable: sDestroyWidth
1077                         * Purpose:  If restoring a table - we should restore it's width
1078                         * Scope:    jQuery.dataTable.classSettings
1079                         */
1080                        this.sDestroyWidth = 0;
1081                       
1082                        /*
1083                         * Variable: fnRowCallback
1084                         * Purpose:  Call this function every time a row is inserted (draw)
1085                         * Scope:    jQuery.dataTable.classSettings
1086                         */
1087                        this.fnRowCallback = null;
1088                       
1089                        /*
1090                         * Variable: fnHeaderCallback
1091                         * Purpose:  Callback function for the header on each draw
1092                         * Scope:    jQuery.dataTable.classSettings
1093                         */
1094                        this.fnHeaderCallback = null;
1095                       
1096                        /*
1097                         * Variable: fnFooterCallback
1098                         * Purpose:  Callback function for the footer on each draw
1099                         * Scope:    jQuery.dataTable.classSettings
1100                         */
1101                        this.fnFooterCallback = null;
1102                       
1103                        /*
1104                         * Variable: aoDrawCallback
1105                         * Purpose:  Array of callback functions for draw callback functions
1106                         * Scope:    jQuery.dataTable.classSettings
1107                         * Notes:    Each array element is an object with the following parameters:
1108                         *   function:fn - function to call
1109                         *   string:sName - name callback (feature). useful for arranging array
1110                         */
1111                        this.aoDrawCallback = [];
1112                       
1113                        /*
1114                         * Variable: fnInitComplete
1115                         * Purpose:  Callback function for when the table has been initalised
1116                         * Scope:    jQuery.dataTable.classSettings
1117                         */
1118                        this.fnInitComplete = null;
1119                       
1120                        /*
1121                         * Variable: sTableId
1122                         * Purpose:  Cache the table ID for quick access
1123                         * Scope:    jQuery.dataTable.classSettings
1124                         */
1125                        this.sTableId = "";
1126                       
1127                        /*
1128                         * Variable: nTable
1129                         * Purpose:  Cache the table node for quick access
1130                         * Scope:    jQuery.dataTable.classSettings
1131                         */
1132                        this.nTable = null;
1133                       
1134                        /*
1135                         * Variable: nTHead
1136                         * Purpose:  Permanent ref to the thead element
1137                         * Scope:    jQuery.dataTable.classSettings
1138                         */
1139                        this.nTHead = null;
1140                       
1141                        /*
1142                         * Variable: nTFoot
1143                         * Purpose:  Permanent ref to the tfoot element - if it exists
1144                         * Scope:    jQuery.dataTable.classSettings
1145                         */
1146                        this.nTFoot = null;
1147                       
1148                        /*
1149                         * Variable: nTBody
1150                         * Purpose:  Permanent ref to the tbody element
1151                         * Scope:    jQuery.dataTable.classSettings
1152                         */
1153                        this.nTBody = null;
1154                       
1155                        /*
1156                         * Variable: nTableWrapper
1157                         * Purpose:  Cache the wrapper node (contains all DataTables controlled elements)
1158                         * Scope:    jQuery.dataTable.classSettings
1159                         */
1160                        this.nTableWrapper = null;
1161                       
1162                        /*
1163                         * Variable: bInitialised
1164                         * Purpose:  Indicate if all required information has been read in
1165                         * Scope:    jQuery.dataTable.classSettings
1166                         */
1167                        this.bInitialised = false;
1168                       
1169                        /*
1170                         * Variable: aoOpenRows
1171                         * Purpose:  Information about open rows
1172                         * Scope:    jQuery.dataTable.classSettings
1173                         * Notes:    Has the parameters 'nTr' and 'nParent'
1174                         */
1175                        this.aoOpenRows = [];
1176                       
1177                        /*
1178                         * Variable: sDom
1179                         * Purpose:  Dictate the positioning that the created elements will take
1180                         * Scope:    jQuery.dataTable.classSettings
1181                         * Notes:   
1182                         *   The following options are allowed:
1183                         *     'l' - Length changing
1184                         *     'f' - Filtering input
1185                         *     't' - The table!
1186                         *     'i' - Information
1187                         *     'p' - Pagination
1188                         *     'r' - pRocessing
1189                         *   The following constants are allowed:
1190                         *     'H' - jQueryUI theme "header" classes
1191                         *     'F' - jQueryUI theme "footer" classes
1192                         *   The following syntax is expected:
1193                         *     '<' and '>' - div elements
1194                         *     '<"class" and '>' - div with a class
1195                         *   Examples:
1196                         *     '<"wrapper"flipt>', '<lf<t>ip>'
1197                         */
1198                        this.sDom = 'lfrtip';
1199                       
1200                        /*
1201                         * Variable: sPaginationType
1202                         * Purpose:  Note which type of sorting should be used
1203                         * Scope:    jQuery.dataTable.classSettings
1204                         */
1205                        this.sPaginationType = "two_button";
1206                       
1207                        /*
1208                         * Variable: iCookieDuration
1209                         * Purpose:  The cookie duration (for bStateSave) in seconds - default 2 hours
1210                         * Scope:    jQuery.dataTable.classSettings
1211                         */
1212                        this.iCookieDuration = 60 * 60 * 2;
1213                       
1214                        /*
1215                         * Variable: sCookiePrefix
1216                         * Purpose:  The cookie name prefix
1217                         * Scope:    jQuery.dataTable.classSettings
1218                         */
1219                        this.sCookiePrefix = "SpryMedia_DataTables_";
1220                       
1221                        /*
1222                         * Variable: fnCookieCallback
1223                         * Purpose:  Callback function for cookie creation
1224                         * Scope:    jQuery.dataTable.classSettings
1225                         */
1226                        this.fnCookieCallback = null;
1227                       
1228                        /*
1229                         * Variable: aoStateSave
1230                         * Purpose:  Array of callback functions for state saving
1231                         * Scope:    jQuery.dataTable.classSettings
1232                         * Notes:    Each array element is an object with the following parameters:
1233                         *   function:fn - function to call. Takes two parameters, oSettings and the JSON string to
1234                         *     save that has been thus far created. Returns a JSON string to be inserted into a
1235                         *     json object (i.e. '"param": [ 0, 1, 2]')
1236                         *   string:sName - name of callback
1237                         */
1238                        this.aoStateSave = [];
1239                       
1240                        /*
1241                         * Variable: aoStateLoad
1242                         * Purpose:  Array of callback functions for state loading
1243                         * Scope:    jQuery.dataTable.classSettings
1244                         * Notes:    Each array element is an object with the following parameters:
1245                         *   function:fn - function to call. Takes two parameters, oSettings and the object stored.
1246                         *     May return false to cancel state loading.
1247                         *   string:sName - name of callback
1248                         */
1249                        this.aoStateLoad = [];
1250                       
1251                        /*
1252                         * Variable: oLoadedState
1253                         * Purpose:  State that was loaded from the cookie. Useful for back reference
1254                         * Scope:    jQuery.dataTable.classSettings
1255                         */
1256                        this.oLoadedState = null;
1257                       
1258                        /*
1259                         * Variable: sAjaxSource
1260                         * Purpose:  Source url for AJAX data for the table
1261                         * Scope:    jQuery.dataTable.classSettings
1262                         */
1263                        this.sAjaxSource = null;
1264                       
1265                        /*
1266                         * Variable: bAjaxDataGet
1267                         * Purpose:  Note if draw should be blocked while getting data
1268                         * Scope:    jQuery.dataTable.classSettings
1269                         */
1270                        this.bAjaxDataGet = true;
1271                       
1272                        /*
1273                         * Variable: fnServerData
1274                         * Purpose:  Function to get the server-side data - can be overruled by the developer
1275                         * Scope:    jQuery.dataTable.classSettings
1276                         */
1277                        this.fnServerData = function ( url, data, callback ) {
1278                                $.ajax( {
1279                                        "url": url,
1280                                        "data": data,
1281                                        "success": callback,
1282                                        "dataType": "json",
1283                                        "cache": false,
1284                                        "error": function (xhr, error, thrown) {
1285                                                if ( error == "parsererror" ) {
1286                                                        alert( "DataTables warning: JSON data from server could not be parsed. "+
1287                                                                "This is caused by a JSON formatting error." );
1288                                                }
1289                                        }
1290                                } );
1291                        };
1292                       
1293                        /*
1294                         * Variable: fnFormatNumber
1295                         * Purpose:  Format numbers for display
1296                         * Scope:    jQuery.dataTable.classSettings
1297                         */
1298                        this.fnFormatNumber = function ( iIn )
1299                        {
1300                                if ( iIn < 1000 )
1301                                {
1302                                        /* A small optimisation for what is likely to be the vast majority of use cases */
1303                                        return iIn;
1304                                }
1305                                else
1306                                {
1307                                        var s=(iIn+""), a=s.split(""), out="", iLen=s.length;
1308                                       
1309                                        for ( var i=0 ; i<iLen ; i++ )
1310                                        {
1311                                                if ( i%3 === 0 && i !== 0 )
1312                                                {
1313                                                        out = ','+out;
1314                                                }
1315                                                out = a[iLen-i-1]+out;
1316                                        }
1317                                }
1318                                return out;
1319                        };
1320                       
1321                        /*
1322                         * Variable: aLengthMenu
1323                         * Purpose:  List of options that can be used for the user selectable length menu
1324                         * Scope:    jQuery.dataTable.classSettings
1325                         * Note:     This varaible can take for form of a 1D array, in which case the value and the
1326                         *   displayed value in the menu are the same, or a 2D array in which case the value comes
1327                         *   from the first array, and the displayed value to the end user comes from the second
1328                         *   array. 2D example: [ [ 10, 25, 50, 100, -1 ], [ 10, 25, 50, 100, 'All' ] ];
1329                         */
1330                        this.aLengthMenu = [ 10, 25, 50, 100 ];
1331                       
1332                        /*
1333                         * Variable: iDraw
1334                         * Purpose:  Counter for the draws that the table does. Also used as a tracker for
1335                         *   server-side processing
1336                         * Scope:    jQuery.dataTable.classSettings
1337                         */
1338                        this.iDraw = 0;
1339                       
1340                        /*
1341                         * Variable: bDrawing
1342                         * Purpose:  Indicate if a redraw is being done - useful for Ajax
1343                         * Scope:    jQuery.dataTable.classSettings
1344                         */
1345                        this.bDrawing = 0;
1346                       
1347                        /*
1348                         * Variable: iDrawError
1349                         * Purpose:  Last draw error
1350                         * Scope:    jQuery.dataTable.classSettings
1351                         */
1352                        this.iDrawError = -1;
1353                       
1354                        /*
1355                         * Variable: _iDisplayLength, _iDisplayStart, _iDisplayEnd
1356                         * Purpose:  Display length variables
1357                         * Scope:    jQuery.dataTable.classSettings
1358                         * Notes:    These variable must NOT be used externally to get the data length. Rather, use
1359                         *   the fnRecordsTotal() (etc) functions.
1360                         */
1361                        this._iDisplayLength = 10;
1362                        this._iDisplayStart = 0;
1363                        this._iDisplayEnd = 10;
1364                       
1365                        /*
1366                         * Variable: _iRecordsTotal, _iRecordsDisplay
1367                         * Purpose:  Display length variables used for server side processing
1368                         * Scope:    jQuery.dataTable.classSettings
1369                         * Notes:    These variable must NOT be used externally to get the data length. Rather, use
1370                         *   the fnRecordsTotal() (etc) functions.
1371                         */
1372                        this._iRecordsTotal = 0;
1373                        this._iRecordsDisplay = 0;
1374                       
1375                        /*
1376                         * Variable: bJUI
1377                         * Purpose:  Should we add the markup needed for jQuery UI theming?
1378                         * Scope:    jQuery.dataTable.classSettings
1379                         */
1380                        this.bJUI = false;
1381                       
1382                        /*
1383                         * Variable: bJUI
1384                         * Purpose:  Should we add the markup needed for jQuery UI theming?
1385                         * Scope:    jQuery.dataTable.classSettings
1386                         */
1387                        this.oClasses = _oExt.oStdClasses;
1388                       
1389                        /*
1390                         * Variable: bFiltered and bSorted
1391                         * Purpose:  Flags to allow callback functions to see what actions have been performed
1392                         * Scope:    jQuery.dataTable.classSettings
1393                         */
1394                        this.bFiltered = false;
1395                        this.bSorted = false;
1396                       
1397                        /*
1398                         * Variable: oInit
1399                         * Purpose:  Initialisation object that is used for the table
1400                         * Scope:    jQuery.dataTable.classSettings
1401                         */
1402                        this.oInit = null;
1403                }
1404               
1405                /*
1406                 * Variable: oApi
1407                 * Purpose:  Container for publicly exposed 'private' functions
1408                 * Scope:    jQuery.dataTable
1409                 */
1410                this.oApi = {};
1411               
1412               
1413                /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1414                 * Section - API functions
1415                 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1416               
1417                /*
1418                 * Function: fnDraw
1419                 * Purpose:  Redraw the table
1420                 * Returns:  -
1421                 * Inputs:   bool:bComplete - Refilter and resort (if enabled) the table before the draw.
1422                 *             Optional: default - true
1423                 */
1424                this.fnDraw = function( bComplete )
1425                {
1426                        var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] );
1427                        if ( typeof bComplete != 'undefined' && bComplete === false )
1428                        {
1429                                _fnCalculateEnd( oSettings );
1430                                _fnDraw( oSettings );
1431                        }
1432                        else
1433                        {
1434                                _fnReDraw( oSettings );
1435                        }
1436                };
1437               
1438                /*
1439                 * Function: fnFilter
1440                 * Purpose:  Filter the input based on data
1441                 * Returns:  -
1442                 * Inputs:   string:sInput - string to filter the table on
1443                 *           int:iColumn - optional - column to limit filtering to
1444                 *           bool:bRegex - optional - treat as regular expression or not - default false
1445                 *           bool:bSmart - optional - perform smart filtering or not - default true
1446                 *           bool:bShowGlobal - optional - show the input global filter in it's input box(es)
1447                 *              - default true
1448                 */
1449                this.fnFilter = function( sInput, iColumn, bRegex, bSmart, bShowGlobal )
1450                {
1451                        var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] );
1452                       
1453                        if ( !oSettings.oFeatures.bFilter )
1454                        {
1455                                return;
1456                        }
1457                       
1458                        if ( typeof bRegex == 'undefined' )
1459                        {
1460                                bRegex = false;
1461                        }
1462                       
1463                        if ( typeof bSmart == 'undefined' )
1464                        {
1465                                bSmart = true;
1466                        }
1467                       
1468                        if ( typeof bShowGlobal == 'undefined' )
1469                        {
1470                                bShowGlobal = true;
1471                        }
1472                       
1473                        if ( typeof iColumn == "undefined" || iColumn === null )
1474                        {
1475                                /* Global filter */
1476                                _fnFilterComplete( oSettings, {
1477                                        "sSearch":sInput,
1478                                        "bRegex": bRegex,
1479                                        "bSmart": bSmart
1480                                }, 1 );
1481                               
1482                                if ( bShowGlobal && typeof oSettings.aanFeatures.f != 'undefined' )
1483                                {
1484                                        var n = oSettings.aanFeatures.f;
1485                                        for ( var i=0, iLen=n.length ; i<iLen ; i++ )
1486                                        {
1487                                                $('input', n[i]).val( sInput );
1488                                        }
1489                                }
1490                        }
1491                        else
1492                        {
1493                                /* Single column filter */
1494                                oSettings.aoPreSearchCols[ iColumn ].sSearch = sInput;
1495                                oSettings.aoPreSearchCols[ iColumn ].bRegex = bRegex;
1496                                oSettings.aoPreSearchCols[ iColumn ].bSmart = bSmart;
1497                                _fnFilterComplete( oSettings, oSettings.oPreviousSearch, 1 );
1498                        }
1499                };
1500               
1501                /*
1502                 * Function: fnSettings
1503                 * Purpose:  Get the settings for a particular table for extern. manipulation
1504                 * Returns:  -
1505                 * Inputs:   -
1506                 */
1507                this.fnSettings = function( nNode  )
1508                {
1509                        return _fnSettingsFromNode( this[_oExt.iApiIndex] );
1510                };
1511               
1512                /*
1513                 * Function: fnVersionCheck
1514                 * Notes:    The function is the same as the 'static' function provided in the ext variable
1515                 */
1516                this.fnVersionCheck = _oExt.fnVersionCheck;
1517               
1518                /*
1519                 * Function: fnSort
1520                 * Purpose:  Sort the table by a particular row
1521                 * Returns:  -
1522                 * Inputs:   int:iCol - the data index to sort on. Note that this will
1523                 *   not match the 'display index' if you have hidden data entries
1524                 */
1525                this.fnSort = function( aaSort )
1526                {
1527                        var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] );
1528                        oSettings.aaSorting = aaSort;
1529                        _fnSort( oSettings );
1530                };
1531               
1532                /*
1533                 * Function: fnSortListener
1534                 * Purpose:  Attach a sort listener to an element for a given column
1535                 * Returns:  -
1536                 * Inputs:   node:nNode - the element to attach the sort listener to
1537                 *           int:iColumn - the column that a click on this node will sort on
1538                 *           function:fnCallback - callback function when sort is run - optional
1539                 */
1540                this.fnSortListener = function( nNode, iColumn, fnCallback )
1541                {
1542                        _fnSortAttachListener( _fnSettingsFromNode( this[_oExt.iApiIndex] ), nNode, iColumn,
1543                                fnCallback );
1544                };
1545               
1546                /*
1547                 * Function: fnAddData
1548                 * Purpose:  Add new row(s) into the table
1549                 * Returns:  array int: array of indexes (aoData) which have been added (zero length on error)
1550                 * Inputs:   array:mData - the data to be added. The length must match
1551                 *               the original data from the DOM
1552                 *             or
1553                 *             array array:mData - 2D array of data to be added
1554                 *           bool:bRedraw - redraw the table or not - default true
1555                 * Notes:    Warning - the refilter here will cause the table to redraw
1556                 *             starting at zero
1557                 * Notes:    Thanks to Yekimov Denis for contributing the basis for this function!
1558                 */
1559                this.fnAddData = function( mData, bRedraw )
1560                {
1561                        if ( mData.length === 0 )
1562                        {
1563                                return [];
1564                        }
1565                       
1566                        var aiReturn = [];
1567                        var iTest;
1568                       
1569                        /* Find settings from table node */
1570                        var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] );
1571                       
1572                        /* Check if we want to add multiple rows or not */
1573                        if ( typeof mData[0] == "object" )
1574                        {
1575                                for ( var i=0 ; i<mData.length ; i++ )
1576                                {
1577                                        iTest = _fnAddData( oSettings, mData[i] );
1578                                        if ( iTest == -1 )
1579                                        {
1580                                                return aiReturn;
1581                                        }
1582                                        aiReturn.push( iTest );
1583                                }
1584                        }
1585                        else
1586                        {
1587                                iTest = _fnAddData( oSettings, mData );
1588                                if ( iTest == -1 )
1589                                {
1590                                        return aiReturn;
1591                                }
1592                                aiReturn.push( iTest );
1593                        }
1594                       
1595                        oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
1596                       
1597                        if ( typeof bRedraw == 'undefined' || bRedraw )
1598                        {
1599                                _fnReDraw( oSettings );
1600                        }
1601                        return aiReturn;
1602                };
1603               
1604                /*
1605                 * Function: fnDeleteRow
1606                 * Purpose:  Remove a row for the table
1607                 * Returns:  array:aReturn - the row that was deleted
1608                 * Inputs:   mixed:mTarget -
1609                 *             int: - index of aoData to be deleted, or
1610                 *             node(TR): - TR element you want to delete
1611                 *           function:fnCallBack - callback function - default null
1612                 *           bool:bRedraw - redraw the table or not - default true
1613                 */
1614                this.fnDeleteRow = function( mTarget, fnCallBack, bRedraw )
1615                {
1616                        /* Find settings from table node */
1617                        var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] );
1618                        var i, iAODataIndex;
1619                       
1620                        iAODataIndex = (typeof mTarget == 'object') ?
1621                                _fnNodeToDataIndex(oSettings, mTarget) : mTarget;
1622                       
1623                        /* Return the data array from this row */
1624                        var oData = oSettings.aoData.splice( iAODataIndex, 1 );
1625                       
1626                        /* Remove the target row from the search array */
1627                        var iDisplayIndex = $.inArray( iAODataIndex, oSettings.aiDisplay );
1628                        oSettings.asDataSearch.splice( iDisplayIndex, 1 );
1629                       
1630                        /* Delete from the display arrays */
1631                        _fnDeleteIndex( oSettings.aiDisplayMaster, iAODataIndex );
1632                        _fnDeleteIndex( oSettings.aiDisplay, iAODataIndex );
1633                       
1634                        /* If there is a user callback function - call it */
1635                        if ( typeof fnCallBack == "function" )
1636                        {
1637                                fnCallBack.call( this, oSettings, oData );
1638                        }
1639                       
1640                        /* Check for an 'overflow' they case for dislaying the table */
1641                        if ( oSettings._iDisplayStart >= oSettings.aiDisplay.length )
1642                        {
1643                                oSettings._iDisplayStart -= oSettings._iDisplayLength;
1644                                if ( oSettings._iDisplayStart < 0 )
1645                                {
1646                                        oSettings._iDisplayStart = 0;
1647                                }
1648                        }
1649                       
1650                        if ( typeof bRedraw == 'undefined' || bRedraw )
1651                        {
1652                                _fnCalculateEnd( oSettings );
1653                                _fnDraw( oSettings );
1654                        }
1655                       
1656                        return oData;
1657                };
1658               
1659                /*
1660                 * Function: fnClearTable
1661                 * Purpose:  Quickly and simply clear a table
1662                 * Returns:  -
1663                 * Inputs:   bool:bRedraw - redraw the table or not - default true
1664                 * Notes:    Thanks to Yekimov Denis for contributing the basis for this function!
1665                 */
1666                this.fnClearTable = function( bRedraw )
1667                {
1668                        /* Find settings from table node */
1669                        var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] );
1670                        _fnClearTable( oSettings );
1671                       
1672                        if ( typeof bRedraw == 'undefined' || bRedraw )
1673                        {
1674                                _fnDraw( oSettings );
1675                        }
1676                };
1677               
1678                /*
1679                 * Function: fnOpen
1680                 * Purpose:  Open a display row (append a row after the row in question)
1681                 * Returns:  node:nNewRow - the row opened
1682                 * Inputs:   node:nTr - the table row to 'open'
1683                 *           string:sHtml - the HTML to put into the row
1684                 *           string:sClass - class to give the new TD cell
1685                 */
1686                this.fnOpen = function( nTr, sHtml, sClass )
1687                {
1688                        /* Find settings from table node */
1689                        var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] );
1690                       
1691                        /* the old open one if there is one */
1692                        this.fnClose( nTr );
1693                       
1694                        var nNewRow = document.createElement("tr");
1695                        var nNewCell = document.createElement("td");
1696                        nNewRow.appendChild( nNewCell );
1697                        nNewCell.className = sClass;
1698                        nNewCell.colSpan = _fnVisbleColumns( oSettings );
1699                        nNewCell.innerHTML = sHtml;
1700                       
1701                        /* If the nTr isn't on the page at the moment - then we don't insert at the moment */
1702                        var nTrs = $('tr', oSettings.nTBody);
1703                        if ( $.inArray(nTr, nTrs) != -1 )
1704                        {
1705                                $(nNewRow).insertAfter(nTr);
1706                        }
1707                       
1708                        oSettings.aoOpenRows.push( {
1709                                "nTr": nNewRow,
1710                                "nParent": nTr
1711                        } );
1712                       
1713                        return nNewRow;
1714                };
1715               
1716                /*
1717                 * Function: fnClose
1718                 * Purpose:  Close a display row
1719                 * Returns:  int: 0 (success) or 1 (failed)
1720                 * Inputs:   node:nTr - the table row to 'close'
1721                 */
1722                this.fnClose = function( nTr )
1723                {
1724                        /* Find settings from table node */
1725                        var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] );
1726                       
1727                        for ( var i=0 ; i<oSettings.aoOpenRows.length ; i++ )
1728                        {
1729                                if ( oSettings.aoOpenRows[i].nParent == nTr )
1730                                {
1731                                        var nTrParent = oSettings.aoOpenRows[i].nTr.parentNode;
1732                                        if ( nTrParent )
1733                                        {
1734                                                /* Remove it if it is currently on display */
1735                                                nTrParent.removeChild( oSettings.aoOpenRows[i].nTr );
1736                                        }
1737                                        oSettings.aoOpenRows.splice( i, 1 );
1738                                        return 0;
1739                                }
1740                        }
1741                        return 1;
1742                };
1743               
1744                /*
1745                 * Function: fnGetData
1746                 * Purpose:  Return an array with the data which is used to make up the table
1747                 * Returns:  array array string: 2d data array ([row][column]) or array string: 1d data array
1748                 *           or
1749                 *           array string (if iRow specified)
1750                 * Inputs:   mixed:mRow - optional - if not present, then the full 2D array for the table
1751                 *             if given then:
1752                 *               int: - return 1D array for aoData entry of this index
1753                 *               node(TR): - return 1D array for this TR element
1754                 * Inputs:   int:iRow - optional - if present then the array returned will be the data for
1755                 *             the row with the index 'iRow'
1756                 */
1757                this.fnGetData = function( mRow )
1758                {
1759                        var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] );
1760                       
1761                        if ( typeof mRow != 'undefined' )
1762                        {
1763                                var iRow = (typeof mRow == 'object') ?
1764                                        _fnNodeToDataIndex(oSettings, mRow) : mRow;
1765                                return ( (aRowData = oSettings.aoData[iRow]) ? aRowData._aData : null);
1766                        }
1767                        return _fnGetDataMaster( oSettings );
1768                };
1769               
1770                /*
1771                 * Function: fnGetNodes
1772                 * Purpose:  Return an array with the TR nodes used for drawing the table
1773                 * Returns:  array node: TR elements
1774                 *           or
1775                 *           node (if iRow specified)
1776                 * Inputs:   int:iRow - optional - if present then the array returned will be the node for
1777                 *             the row with the index 'iRow'
1778                 */
1779                this.fnGetNodes = function( iRow )
1780                {
1781                        var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] );
1782                       
1783                        if ( typeof iRow != 'undefined' )
1784                        {
1785                                return ( (aRowData = oSettings.aoData[iRow]) ? aRowData.nTr : null );
1786                        }
1787                        return _fnGetTrNodes( oSettings );
1788                };
1789               
1790                /*
1791                 * Function: fnGetPosition
1792                 * Purpose:  Get the array indexes of a particular cell from it's DOM element
1793                 * Returns:  int: - row index, or array[ int, int, int ]: - row index, column index (visible)
1794                 *             and column index including hidden columns
1795                 * Inputs:   node:nNode - this can either be a TR or a TD in the table, the return is
1796                 *             dependent on this input
1797                 */
1798                this.fnGetPosition = function( nNode )
1799                {
1800                        var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] );
1801                        var i;
1802                       
1803                        if ( nNode.nodeName.toUpperCase() == "TR" )
1804                        {
1805                                return _fnNodeToDataIndex(oSettings, nNode);
1806                        }
1807                        else if ( nNode.nodeName.toUpperCase() == "TD" )
1808                        {
1809                                var iDataIndex = _fnNodeToDataIndex(oSettings, nNode.parentNode);
1810                                var iCorrector = 0;
1811                                for ( var j=0 ; j<oSettings.aoColumns.length ; j++ )
1812                                {
1813                                        if ( oSettings.aoColumns[j].bVisible )
1814                                        {
1815                                                if ( oSettings.aoData[iDataIndex].nTr.getElementsByTagName('td')[j-iCorrector] == nNode )
1816                                                {
1817                                                        return [ iDataIndex, j-iCorrector, j ];
1818                                                }
1819                                        }
1820                                        else
1821                                        {
1822                                                iCorrector++;
1823                                        }
1824                                }
1825                        }
1826                        return null;
1827                };
1828               
1829                /*
1830                 * Function: fnUpdate
1831                 * Purpose:  Update a table cell or row
1832                 * Returns:  int: 0 okay, 1 error
1833                 * Inputs:   array string 'or' string:mData - data to update the cell/row with
1834                 *           mixed:mRow -
1835                 *             int: - index of aoData to be updated, or
1836                 *             node(TR): - TR element you want to update
1837                 *           int:iColumn - the column to update - optional (not used of mData is 2D)
1838                 *           bool:bRedraw - redraw the table or not - default true
1839                 *           bool:bAction - perform predraw actions or not (you will want this as 'true' if
1840                 *             you have bRedraw as true) - default true
1841                 */
1842                this.fnUpdate = function( mData, mRow, iColumn, bRedraw, bAction )
1843                {
1844                        var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] );
1845                        var iVisibleColumn;
1846                        var sDisplay;
1847                        var iRow = (typeof mRow == 'object') ?
1848                                _fnNodeToDataIndex(oSettings, mRow) : mRow;
1849                       
1850                        if ( typeof mData != 'object' )
1851                        {
1852                                sDisplay = mData;
1853                                oSettings.aoData[iRow]._aData[iColumn] = sDisplay;
1854                               
1855                                if ( oSettings.aoColumns[iColumn].fnRender !== null )
1856                                {
1857                                        sDisplay = oSettings.aoColumns[iColumn].fnRender( {
1858                                                "iDataRow": iRow,
1859                                                "iDataColumn": iColumn,
1860                                                "aData": oSettings.aoData[iRow]._aData,
1861                                                "oSettings": oSettings
1862                                        } );
1863                                       
1864                                        if ( oSettings.aoColumns[iColumn].bUseRendered )
1865                                        {
1866                                                oSettings.aoData[iRow]._aData[iColumn] = sDisplay;
1867                                        }
1868                                }
1869                               
1870                                iVisibleColumn = _fnColumnIndexToVisible( oSettings, iColumn );
1871                                if ( iVisibleColumn !== null )
1872                                {
1873                                        oSettings.aoData[iRow].nTr.getElementsByTagName('td')[iVisibleColumn].innerHTML =
1874                                                sDisplay;
1875                                }
1876                                else
1877                                {
1878                                        oSettings.aoData[iRow]._anHidden[iColumn].innerHTML = sDisplay;
1879                                }
1880                        }
1881                        else
1882                        {
1883                                if ( mData.length != oSettings.aoColumns.length )
1884                                {
1885                                        _fnLog( oSettings, 0, 'An array passed to fnUpdate must have the same number of '+
1886                                                'columns as the table in question - in this case '+oSettings.aoColumns.length );
1887                                        return 1;
1888                                }
1889                               
1890                                for ( var i=0 ; i<mData.length ; i++ )
1891                                {
1892                                        sDisplay = mData[i];
1893                                        oSettings.aoData[iRow]._aData[i] = sDisplay;
1894                                       
1895                                        if ( oSettings.aoColumns[i].fnRender !== null )
1896                                        {
1897                                                sDisplay = oSettings.aoColumns[i].fnRender( {
1898                                                        "iDataRow": iRow,
1899                                                        "iDataColumn": i,
1900                                                        "aData": oSettings.aoData[iRow]._aData,
1901                                                        "oSettings": oSettings
1902                                                } );
1903                                               
1904                                                if ( oSettings.aoColumns[i].bUseRendered )
1905                                                {
1906                                                        oSettings.aoData[iRow]._aData[i] = sDisplay;
1907                                                }
1908                                        }
1909                                       
1910                                        iVisibleColumn = _fnColumnIndexToVisible( oSettings, i );
1911                                        if ( iVisibleColumn !== null )
1912                                        {
1913                                                oSettings.aoData[iRow].nTr.getElementsByTagName('td')[iVisibleColumn].innerHTML =
1914                                                        sDisplay;
1915                                        }
1916                                        else
1917                                        {
1918                                                oSettings.aoData[iRow]._anHidden[i].innerHTML = sDisplay;
1919                                        }
1920                                }
1921                        }
1922                       
1923                        /* Modify the search index for this row (strictly this is likely not needed, since fnReDraw
1924                         * will rebuild the search array - however, the redraw might be disabled by the user)
1925                         */
1926                        var iDisplayIndex = $.inArray( iRow, oSettings.aiDisplay );
1927                        oSettings.asDataSearch[iDisplayIndex] = _fnBuildSearchRow( oSettings,
1928                                oSettings.aoData[iRow]._aData );
1929                       
1930                        /* Perform pre-draw actions */
1931                        if ( typeof bAction == 'undefined' || bAction )
1932                        {
1933                                _fnAjustColumnSizing( oSettings );
1934                        }
1935                       
1936                        /* Redraw the table */
1937                        if ( typeof bRedraw == 'undefined' || bRedraw )
1938                        {
1939                                _fnReDraw( oSettings );
1940                        }
1941                        return 0;
1942                };
1943               
1944               
1945                /*
1946                 * Function: fnShowColoumn
1947                 * Purpose:  Show a particular column
1948                 * Returns:  -
1949                 * Inputs:   int:iCol - the column whose display should be changed
1950                 *           bool:bShow - show (true) or hide (false) the column
1951                 *           bool:bRedraw - redraw the table or not - default true
1952                 */
1953                this.fnSetColumnVis = function ( iCol, bShow, bRedraw )
1954                {
1955                        var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] );
1956                        var i, iLen;
1957                        var iColumns = oSettings.aoColumns.length;
1958                        var nTd, anTds, nCell, anTrs, jqChildren;
1959                       
1960                        /* No point in doing anything if we are requesting what is already true */
1961                        if ( oSettings.aoColumns[iCol].bVisible == bShow )
1962                        {
1963                                return;
1964                        }
1965                       
1966                        var nTrHead = $('>tr', oSettings.nTHead)[0];
1967                        var nTrFoot = $('>tr', oSettings.nTFoot)[0];
1968                        var anTheadTh = [];
1969                        var anTfootTh = [];
1970                        for ( i=0 ; i<iColumns ; i++ )
1971                        {
1972                                anTheadTh.push( oSettings.aoColumns[i].nTh );
1973                                anTfootTh.push( oSettings.aoColumns[i].nTf );
1974                        }
1975                       
1976                        /* Show the column */
1977                        if ( bShow )
1978                        {
1979                                var iInsert = 0;
1980                                for ( i=0 ; i<iCol ; i++ )
1981                                {
1982                                        if ( oSettings.aoColumns[i].bVisible )
1983                                        {
1984                                                iInsert++;
1985                                        }
1986                                }
1987                               
1988                                /* Need to decide if we should use appendChild or insertBefore */
1989                                if ( iInsert >= _fnVisbleColumns( oSettings ) )
1990                                {
1991                                        nTrHead.appendChild( anTheadTh[iCol] );
1992                                        anTrs = $('>tr', oSettings.nTHead);
1993                                        for ( i=1, iLen=anTrs.length ; i<iLen ; i++ )
1994                                        {
1995                                                anTrs[i].appendChild( oSettings.aoColumns[iCol].anThExtra[i-1] );
1996                                        }       
1997                                       
1998                                        if ( nTrFoot )
1999                                        {
2000                                                nTrFoot.appendChild( anTfootTh[iCol] );
2001                                                anTrs = $('>tr', oSettings.nTFoot);
2002                                                for ( i=1, iLen=anTrs.length ; i<iLen ; i++ )
2003                                                {
2004                                                        anTrs[i].appendChild( oSettings.aoColumns[iCol].anTfExtra[i-1] );
2005                                                }       
2006                                        }
2007                                       
2008                                        for ( i=0, iLen=oSettings.aoData.length ; i<iLen ; i++ )
2009                                        {
2010                                                nTd = oSettings.aoData[i]._anHidden[iCol];
2011                                                oSettings.aoData[i].nTr.appendChild( nTd );
2012                                        }
2013                                }
2014                                else
2015                                {
2016                                        /* Which coloumn should we be inserting before? */
2017                                        var iBefore;
2018                                        for ( i=iCol ; i<iColumns ; i++ )
2019                                        {
2020                                                iBefore = _fnColumnIndexToVisible( oSettings, i );
2021                                                if ( iBefore !== null )
2022                                                {
2023                                                        break;
2024                                                }
2025                                        }
2026                                       
2027                                        nTrHead.insertBefore( anTheadTh[iCol], nTrHead.getElementsByTagName('th')[iBefore] );
2028                                        anTrs = $('>tr', oSettings.nTHead);
2029                                        for ( i=1, iLen=anTrs.length ; i<iLen ; i++ )
2030                                        {
2031                                                jqChildren = $(anTrs[i]).children();
2032                                                anTrs[i].insertBefore( oSettings.aoColumns[iCol].anThExtra[i-1], jqChildren[iBefore] );
2033                                        }       
2034                                       
2035                                        if ( nTrFoot )
2036                                        {
2037                                                nTrFoot.insertBefore( anTfootTh[iCol], nTrFoot.getElementsByTagName('th')[iBefore] );
2038                                                anTrs = $('>tr', oSettings.nTFoot);
2039                                                for ( i=1, iLen=anTrs.length ; i<iLen ; i++ )
2040                                                {
2041                                                        jqChildren = $(anTrs[i]).children();
2042                                                        anTrs[i].insertBefore( oSettings.aoColumns[iCol].anTfExtra[i-1], jqChildren[iBefore] );
2043                                                }       
2044                                        }
2045                                       
2046                                        anTds = _fnGetTdNodes( oSettings );
2047                                        for ( i=0, iLen=oSettings.aoData.length ; i<iLen ; i++ )
2048                                        {
2049                                                nTd = oSettings.aoData[i]._anHidden[iCol];
2050                                                oSettings.aoData[i].nTr.insertBefore( nTd, $('>td:eq('+iBefore+')',
2051                                                        oSettings.aoData[i].nTr)[0] );
2052                                        }
2053                                }
2054                               
2055                                oSettings.aoColumns[iCol].bVisible = true;
2056                        }
2057                        else
2058                        {
2059                                /* Remove a column from display */
2060                                nTrHead.removeChild( anTheadTh[iCol] );
2061                                for ( i=0, iLen=oSettings.aoColumns[iCol].anThExtra.length ; i<iLen ; i++ )
2062                                {
2063                                        nCell = oSettings.aoColumns[iCol].anThExtra[i];
2064                                        nCell.parentNode.removeChild( nCell );
2065                                }
2066                               
2067                                if ( nTrFoot )
2068                                {
2069                                        nTrFoot.removeChild( anTfootTh[iCol] );
2070                                        for ( i=0, iLen=oSettings.aoColumns[iCol].anTfExtra.length ; i<iLen ; i++ )
2071                                        {
2072                                                nCell = oSettings.aoColumns[iCol].anTfExtra[i];
2073                                                nCell.parentNode.removeChild( nCell );
2074                                        }
2075                                }
2076                               
2077                                anTds = _fnGetTdNodes( oSettings );
2078                                for ( i=0, iLen=oSettings.aoData.length ; i<iLen ; i++ )
2079                                {
2080                                        nTd = anTds[ ( i*oSettings.aoColumns.length) + (iCol*1) ];
2081                                        oSettings.aoData[i]._anHidden[iCol] = nTd;
2082                                        nTd.parentNode.removeChild( nTd );
2083                                }
2084                               
2085                                oSettings.aoColumns[iCol].bVisible = false;
2086                        }
2087                       
2088                        /* If there are any 'open' rows, then we need to alter the colspan for this col change */
2089                        for ( i=0, iLen=oSettings.aoOpenRows.length ; i<iLen ; i++ )
2090                        {
2091                                oSettings.aoOpenRows[i].nTr.colSpan = _fnVisbleColumns( oSettings );
2092                        }
2093                       
2094                        /* Do a redraw incase anything depending on the table columns needs it
2095                         * (built-in: scrolling)
2096                         */
2097                        if ( typeof bRedraw == 'undefined' || bRedraw )
2098                        {
2099                                _fnAjustColumnSizing( oSettings );
2100                                _fnDraw( oSettings );
2101                        }
2102                       
2103                        _fnSaveState( oSettings );
2104                };
2105               
2106                /*
2107                 * Function: fnPageChange
2108                 * Purpose:  Change the pagination
2109                 * Returns:  -
2110                 * Inputs:   string:sAction - paging action to take: "first", "previous", "next" or "last"
2111                 *           bool:bRedraw - redraw the table or not - optional - default true
2112                 */
2113                this.fnPageChange = function ( sAction, bRedraw )
2114                {
2115                        var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] );
2116                        _fnPageChange( oSettings, sAction );
2117                        _fnCalculateEnd( oSettings );
2118                       
2119                        if ( typeof bRedraw == 'undefined' || bRedraw )
2120                        {
2121                                _fnDraw( oSettings );
2122                        }
2123                };
2124               
2125                /*
2126                 * Function: fnDestroy
2127                 * Purpose:  Destructor for the DataTable
2128                 * Returns:  -
2129                 * Inputs:   -
2130                 */
2131                this.fnDestroy = function ( )
2132                {
2133                        var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] );
2134                        var nOrig = oSettings.nTableWrapper.parentNode;
2135                        var nBody = oSettings.nTBody;
2136                        var i, iLen;
2137                       
2138                        /* Flag to note that the table is currently being destoryed - no action should be taken */
2139                        oSettings.bDestroying = true;
2140                       
2141                        /* Blitz all DT events */
2142                        $(oSettings.nTableWrapper).find('*').andSelf().unbind('.DT');
2143                       
2144                        /* Restore hidden columns */
2145                        for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
2146                        {
2147                                if ( oSettings.aoColumns[i].bVisible === false )
2148                                {
2149                                        this.fnSetColumnVis( i, true );
2150                                }
2151                        }
2152                       
2153                        /* If there is an 'empty' indicator row, remove it */
2154                        $('tbody>tr>td.'+oSettings.oClasses.sRowEmpty, oSettings.nTable).parent().remove();
2155                       
2156                        /* When scrolling we had to break the table up - restore it */
2157                        if ( oSettings.nTable != oSettings.nTHead.parentNode )
2158                        {
2159                                $('>thead', oSettings.nTable).remove();
2160                                oSettings.nTable.appendChild( oSettings.nTHead );
2161                        }
2162                       
2163                        if ( oSettings.nTFoot && oSettings.nTable != oSettings.nTFoot.parentNode )
2164                        {
2165                                $('>tfoot', oSettings.nTable).remove();
2166                                oSettings.nTable.appendChild( oSettings.nTFoot );
2167                        }
2168                       
2169                        /* Remove the DataTables generated nodes, events and classes */
2170                        oSettings.nTable.parentNode.removeChild( oSettings.nTable );
2171                        $(oSettings.nTableWrapper).remove();
2172                       
2173                        oSettings.aaSorting = [];
2174                        oSettings.aaSortingFixed = [];
2175                        _fnSortingClasses( oSettings );
2176                       
2177                        $(_fnGetTrNodes( oSettings )).removeClass( oSettings.asStripClasses.join(' ') );
2178                       
2179                        if ( !oSettings.bJUI )
2180                        {
2181                                $('th', oSettings.nTHead).removeClass( [ _oExt.oStdClasses.sSortable,
2182                                        _oExt.oStdClasses.sSortableAsc,
2183                                        _oExt.oStdClasses.sSortableDesc,
2184                                        _oExt.oStdClasses.sSortableNone ].join(' ')
2185                                );
2186                        }
2187                        else
2188                        {
2189                                $('th', oSettings.nTHead).removeClass( [ _oExt.oStdClasses.sSortable,
2190                                        _oExt.oJUIClasses.sSortableAsc,
2191                                        _oExt.oJUIClasses.sSortableDesc,
2192                                        _oExt.oJUIClasses.sSortableNone ].join(' ')
2193                                );
2194                                $('th span', oSettings.nTHead).remove();
2195                        }
2196                       
2197                        /* Add the TR elements back into the table in their original order */
2198                        nOrig.appendChild( oSettings.nTable );
2199                        for ( i=0, iLen=oSettings.aoData.length ; i<iLen ; i++ )
2200                        {
2201                                nBody.appendChild( oSettings.aoData[i].nTr );
2202                        }
2203                       
2204                        /* Restore the width of the original table */
2205                        oSettings.nTable.style.width = _fnStringToCss(oSettings.sDestroyWidth);
2206                       
2207                        /* If the were originally odd/even type classes - then we add them back here. Note
2208                         * this is not fool proof (for example if not all rows as odd/even classes - but
2209                         * it's a good effort without getting carried away
2210                         */
2211                        $('>tr:even', nBody).addClass( oSettings.asDestoryStrips[0] );
2212                        $('>tr:odd', nBody).addClass( oSettings.asDestoryStrips[1] );
2213                       
2214                        /* Remove the settings object from the settings array */
2215                        for ( i=0, iLen=_aoSettings.length ; i<iLen ; i++ )
2216                        {
2217                                if ( _aoSettings[i] == oSettings )
2218                                {
2219                                        _aoSettings.splice( i, 1 );
2220                                }
2221                        }
2222                       
2223                        /* End it all */
2224                        oSettings = null;
2225                };
2226               
2227                /*
2228                 * Function: fnAjustColumnSizing
2229                 * Purpose:  Update tale sizing based on content. This would most likely be used for scrolling
2230                 *   and will typically need a redraw after it.
2231                 * Returns:  -
2232                 * Inputs:   bool:bRedraw - redraw the table or not, you will typically want to - default true
2233                 */
2234                this.fnAdjustColumnSizing = function ( bRedraw )
2235                {
2236                        var oSettings = _fnSettingsFromNode(this[_oExt.iApiIndex]);
2237                        _fnAjustColumnSizing( oSettings );
2238                       
2239                        if ( typeof bRedraw == 'undefined' || bRedraw )
2240                        {
2241                                this.fnDraw( false );
2242                        }
2243                        else if ( oSettings.oScroll.sX !== "" || oSettings.oScroll.sY !== "" )
2244                        {
2245                                /* If not redrawing, but scrolling, we want to apply the new column sizes anyway */
2246                                this.oApi._fnScrollDraw(oSettings);
2247                        }
2248                };
2249               
2250                /*
2251                 * Plugin API functions
2252                 *
2253                 * This call will add the functions which are defined in _oExt.oApi to the
2254                 * DataTables object, providing a rather nice way to allow plug-in API functions. Note that
2255                 * this is done here, so that API function can actually override the built in API functions if
2256                 * required for a particular purpose.
2257                 */
2258               
2259                /*
2260                 * Function: _fnExternApiFunc
2261                 * Purpose:  Create a wrapper function for exporting an internal func to an external API func
2262                 * Returns:  function: - wrapped function
2263                 * Inputs:   string:sFunc - API function name
2264                 */
2265                function _fnExternApiFunc (sFunc)
2266                {
2267                        return function() {
2268                                        var aArgs = [_fnSettingsFromNode(this[_oExt.iApiIndex])].concat(
2269                                                Array.prototype.slice.call(arguments) );
2270                                        return _oExt.oApi[sFunc].apply( this, aArgs );
2271                                };
2272                }
2273               
2274                for ( var sFunc in _oExt.oApi )
2275                {
2276                        if ( sFunc )
2277                        {
2278                                /*
2279                                 * Function: anon
2280                                 * Purpose:  Wrap the plug-in API functions in order to provide the settings as 1st arg
2281                                 *   and execute in this scope
2282                                 * Returns:  -
2283                                 * Inputs:   -
2284                                 */
2285                                this[sFunc] = _fnExternApiFunc(sFunc);
2286                        }
2287                }
2288               
2289               
2290               
2291                /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2292                 * Section - Local functions
2293                 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2294               
2295                /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2296                 * Section - Initalisation
2297                 */
2298               
2299                /*
2300                 * Function: _fnInitalise
2301                 * Purpose:  Draw the table for the first time, adding all required features
2302                 * Returns:  -
2303                 * Inputs:   object:oSettings - dataTables settings object
2304                 */
2305                function _fnInitalise ( oSettings )
2306                {
2307                        var i, iLen;
2308                       
2309                        /* Ensure that the table data is fully initialised */
2310                        if ( oSettings.bInitialised === false )
2311                        {
2312                                setTimeout( function(){ _fnInitalise( oSettings ); }, 200 );
2313                                return;
2314                        }
2315                       
2316                        /* Show the display HTML options */
2317                        _fnAddOptionsHtml( oSettings );
2318                       
2319                        /* Draw the headers for the table */
2320                        _fnDrawHead( oSettings );
2321                       
2322                        /* Okay to show that something is going on now */
2323                        _fnProcessingDisplay( oSettings, true );
2324                       
2325                        /* Calculate sizes for columns */
2326                        if ( oSettings.oFeatures.bAutoWidth )
2327                        {
2328                                _fnCalculateColumnWidths( oSettings );
2329                        }
2330                       
2331                        for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
2332                        {
2333                                if ( oSettings.aoColumns[i].sWidth !== null )
2334                                {
2335                                        oSettings.aoColumns[i].nTh.style.width = _fnStringToCss( oSettings.aoColumns[i].sWidth );
2336                                }
2337                        }
2338                       
2339                        /* If there is default sorting required - let's do it. The sort function will do the
2340                         * drawing for us. Otherwise we draw the table regardless of the Ajax source - this allows
2341                         * the table to look initialised for Ajax sourcing data (show 'loading' message possibly)
2342                         */
2343                        if ( oSettings.oFeatures.bSort )
2344                        {
2345                                _fnSort( oSettings );
2346                        }
2347                        else
2348                        {
2349                                oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
2350                                _fnCalculateEnd( oSettings );
2351                                _fnDraw( oSettings );
2352                        }
2353                       
2354                        /* if there is an ajax source load the data */
2355                        if ( oSettings.sAjaxSource !== null && !oSettings.oFeatures.bServerSide )
2356                        {
2357                                oSettings.fnServerData.call( oSettings.oInstance, oSettings.sAjaxSource, [], function(json) {
2358                                        /* Got the data - add it to the table */
2359                                        for ( i=0 ; i<json.aaData.length ; i++ )
2360                                        {
2361                                                _fnAddData( oSettings, json.aaData[i] );
2362                                        }
2363                                       
2364                                        /* Reset the init display for cookie saving. We've already done a filter, and
2365                                         * therefore cleared it before. So we need to make it appear 'fresh'
2366                                         */
2367                                        oSettings.iInitDisplayStart = oSettings._iDisplayStart;
2368                                       
2369                                        if ( oSettings.oFeatures.bSort )
2370                                        {
2371                                                _fnSort( oSettings );
2372                                        }
2373                                        else
2374                                        {
2375                                                oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
2376                                                _fnCalculateEnd( oSettings );
2377                                                _fnDraw( oSettings );
2378                                        }
2379                                       
2380                                        _fnProcessingDisplay( oSettings, false );
2381                                        _fnInitComplete( oSettings, json );
2382                                } );
2383                                return;
2384                        }
2385                       
2386                        /* Server-side processing initialisation complete is done at the end of _fnDraw */
2387                        if ( !oSettings.oFeatures.bServerSide )
2388                        {
2389                                _fnProcessingDisplay( oSettings, false );
2390                                _fnInitComplete( oSettings );
2391                        }
2392                }
2393               
2394                /*
2395                 * Function: _fnInitalise
2396                 * Purpose:  Draw the table for the first time, adding all required features
2397                 * Returns:  -
2398                 * Inputs:   object:oSettings - dataTables settings object
2399                 */
2400                function _fnInitComplete ( oSettings, json )
2401                {
2402                        oSettings._bInitComplete = true;
2403                        if ( typeof oSettings.fnInitComplete == 'function' )
2404                        {
2405                                if ( typeof json != 'undefined' )
2406                                {
2407                                        oSettings.fnInitComplete.call( oSettings.oInstance, oSettings, json );
2408                                }
2409                                else
2410                                {
2411                                        oSettings.fnInitComplete.call( oSettings.oInstance, oSettings );
2412                                }
2413                        }
2414                }
2415               
2416                /*
2417                 * Function: _fnLanguageProcess
2418                 * Purpose:  Copy language variables from remote object to a local one
2419                 * Returns:  -
2420                 * Inputs:   object:oSettings - dataTables settings object
2421                 *           object:oLanguage - Language information
2422                 *           bool:bInit - init once complete
2423                 */
2424                function _fnLanguageProcess( oSettings, oLanguage, bInit )
2425                {
2426                        _fnMap( oSettings.oLanguage, oLanguage, 'sProcessing' );
2427                        _fnMap( oSettings.oLanguage, oLanguage, 'sLengthMenu' );
2428                        _fnMap( oSettings.oLanguage, oLanguage, 'sEmptyTable' );
2429                        _fnMap( oSettings.oLanguage, oLanguage, 'sZeroRecords' );
2430                        _fnMap( oSettings.oLanguage, oLanguage, 'sInfo' );
2431                        _fnMap( oSettings.oLanguage, oLanguage, 'sInfoEmpty' );
2432                        _fnMap( oSettings.oLanguage, oLanguage, 'sInfoFiltered' );
2433                        _fnMap( oSettings.oLanguage, oLanguage, 'sInfoPostFix' );
2434                        _fnMap( oSettings.oLanguage, oLanguage, 'sSearch' );
2435                       
2436                        if ( typeof oLanguage.oPaginate != 'undefined' )
2437                        {
2438                                _fnMap( oSettings.oLanguage.oPaginate, oLanguage.oPaginate, 'sFirst' );
2439                                _fnMap( oSettings.oLanguage.oPaginate, oLanguage.oPaginate, 'sPrevious' );
2440                                _fnMap( oSettings.oLanguage.oPaginate, oLanguage.oPaginate, 'sNext' );
2441                                _fnMap( oSettings.oLanguage.oPaginate, oLanguage.oPaginate, 'sLast' );
2442                        }
2443                       
2444                        /* Backwards compatibility - if there is no sEmptyTable given, then use the same as
2445                         * sZeroRecords - assuming that is given.
2446                         */
2447                        if ( typeof oLanguage.sEmptyTable == 'undefined' &&
2448                             typeof oLanguage.sZeroRecords != 'undefined' )
2449                        {
2450                                _fnMap( oSettings.oLanguage, oLanguage, 'sZeroRecords', 'sEmptyTable' );
2451                        }
2452                       
2453                        if ( bInit )
2454                        {
2455                                _fnInitalise( oSettings );
2456                        }
2457                }
2458               
2459                /*
2460                 * Function: _fnAddColumn
2461                 * Purpose:  Add a column to the list used for the table with default values
2462                 * Returns:  -
2463                 * Inputs:   object:oSettings - dataTables settings object
2464                 *           node:nTh - the th element for this column
2465                 */
2466                function _fnAddColumn( oSettings, nTh )
2467                {
2468                        oSettings.aoColumns[ oSettings.aoColumns.length++ ] = {
2469                                "sType": null,
2470                                "_bAutoType": true,
2471                                "bVisible": true,
2472                                "bSearchable": true,
2473                                "bSortable": true,
2474                                "asSorting": [ 'asc', 'desc' ],
2475                                "sSortingClass": oSettings.oClasses.sSortable,
2476                                "sSortingClassJUI": oSettings.oClasses.sSortJUI,
2477                                "sTitle": nTh ? nTh.innerHTML : '',
2478                                "sName": '',
2479                                "sWidth": null,
2480                                "sWidthOrig": null,
2481                                "sClass": null,
2482                                "fnRender": null,
2483                                "bUseRendered": true,
2484                                "iDataSort": oSettings.aoColumns.length-1,
2485                                "sSortDataType": 'std',
2486                                "nTh": nTh ? nTh : document.createElement('th'),
2487                                "nTf": null,
2488                                "anThExtra": [],
2489                                "anTfExtra": []
2490                        };
2491                       
2492                        var iCol = oSettings.aoColumns.length-1;
2493                        var oCol = oSettings.aoColumns[ iCol ];
2494                       
2495                        /* Add a column specific filter */
2496                        if ( typeof oSettings.aoPreSearchCols[ iCol ] == 'undefined' ||
2497                             oSettings.aoPreSearchCols[ iCol ] === null )
2498                        {
2499                                oSettings.aoPreSearchCols[ iCol ] = {
2500                                        "sSearch": "",
2501                                        "bRegex": false,
2502                                        "bSmart": true
2503                                };
2504                        }
2505                        else
2506                        {
2507                                /* Don't require that the user must specify bRegex and / or bSmart */
2508                                if ( typeof oSettings.aoPreSearchCols[ iCol ].bRegex == 'undefined' )
2509                                {
2510                                        oSettings.aoPreSearchCols[ iCol ].bRegex = true;
2511                                }
2512                               
2513                                if ( typeof oSettings.aoPreSearchCols[ iCol ].bSmart == 'undefined' )
2514                                {
2515                                        oSettings.aoPreSearchCols[ iCol ].bSmart = true;
2516                                }
2517                        }
2518                       
2519                        /* Use the column options function to initialise classes etc */
2520                        _fnColumnOptions( oSettings, iCol, null );
2521                }
2522               
2523                /*
2524                 * Function: _fnColumnOptions
2525                 * Purpose:  Apply options for a column
2526                 * Returns:  -
2527                 * Inputs:   object:oSettings - dataTables settings object
2528                 *           int:iCol - column index to consider
2529                 *           object:oOptions - object with sType, bVisible and bSearchable
2530                 */
2531                function _fnColumnOptions( oSettings, iCol, oOptions )
2532                {
2533                        var oCol = oSettings.aoColumns[ iCol ];
2534                       
2535                        /* User specified column options */
2536                        if ( typeof oOptions != 'undefined' && oOptions !== null )
2537                        {
2538                                if ( typeof oOptions.sType != 'undefined' )
2539                                {
2540                                        oCol.sType = oOptions.sType;
2541                                        oCol._bAutoType = false;
2542                                }
2543                               
2544                                _fnMap( oCol, oOptions, "bVisible" );
2545                                _fnMap( oCol, oOptions, "bSearchable" );
2546                                _fnMap( oCol, oOptions, "bSortable" );
2547                                _fnMap( oCol, oOptions, "sTitle" );
2548                                _fnMap( oCol, oOptions, "sName" );
2549                                _fnMap( oCol, oOptions, "sWidth" );
2550                                _fnMap( oCol, oOptions, "sWidth", "sWidthOrig" );
2551                                _fnMap( oCol, oOptions, "sClass" );
2552                                _fnMap( oCol, oOptions, "fnRender" );
2553                                _fnMap( oCol, oOptions, "bUseRendered" );
2554                                _fnMap( oCol, oOptions, "iDataSort" );
2555                                _fnMap( oCol, oOptions, "asSorting" );
2556                                _fnMap( oCol, oOptions, "sSortDataType" );
2557                        }
2558                       
2559                        /* Feature sorting overrides column specific when off */
2560                        if ( !oSettings.oFeatures.bSort )
2561                        {
2562                                oCol.bSortable = false;
2563                        }
2564                       
2565                        /* Check that the class assignment is correct for sorting */
2566                        if ( !oCol.bSortable ||
2567                                         ($.inArray('asc', oCol.asSorting) == -1 && $.inArray('desc', oCol.asSorting) == -1) )
2568                        {
2569                                oCol.sSortingClass = oSettings.oClasses.sSortableNone;
2570                                oCol.sSortingClassJUI = "";
2571                        }
2572                        else if ( $.inArray('asc', oCol.asSorting) != -1 && $.inArray('desc', oCol.asSorting) == -1 )
2573                        {
2574                                oCol.sSortingClass = oSettings.oClasses.sSortableAsc;
2575                                oCol.sSortingClassJUI = oSettings.oClasses.sSortJUIAscAllowed;
2576                        }
2577                        else if ( $.inArray('asc', oCol.asSorting) == -1 && $.inArray('desc', oCol.asSorting) != -1 )
2578                        {
2579                                oCol.sSortingClass = oSettings.oClasses.sSortableDesc;
2580                                oCol.sSortingClassJUI = oSettings.oClasses.sSortJUIDescAllowed;
2581                        }
2582                }
2583               
2584                /*
2585                 * Function: _fnAddData
2586                 * Purpose:  Add a data array to the table, creating DOM node etc
2587                 * Returns:  int: - >=0 if successful (index of new aoData entry), -1 if failed
2588                 * Inputs:   object:oSettings - dataTables settings object
2589                 *           array:aData - data array to be added
2590                 * Notes:    There are two basic methods for DataTables to get data to display - a JS array
2591                 *   (which is dealt with by this function), and the DOM, which has it's own optimised
2592                 *   function (_fnGatherData). Be careful to make the same changes here as there and vice-versa
2593                 */
2594                function _fnAddData ( oSettings, aDataSupplied )
2595                {
2596                        /* Sanity check the length of the new array */
2597                        if ( aDataSupplied.length != oSettings.aoColumns.length &&
2598                                oSettings.iDrawError != oSettings.iDraw )
2599                        {
2600                                _fnLog( oSettings, 0, "Added data (size "+aDataSupplied.length+") does not match known "+
2601                                        "number of columns ("+oSettings.aoColumns.length+")" );
2602                                oSettings.iDrawError = oSettings.iDraw;
2603                                return -1;
2604                        }
2605                       
2606                       
2607                        /* Create the object for storing information about this new row */
2608                        var aData = aDataSupplied.slice();
2609                        var iThisIndex = oSettings.aoData.length;
2610                        oSettings.aoData.push( {
2611                                "nTr": document.createElement('tr'),
2612                                "_iId": oSettings.iNextId++,
2613                                "_aData": aData,
2614                                "_anHidden": [],
2615                                "_sRowStripe": ''
2616                        } );
2617                       
2618                        /* Create the cells */
2619                        var nTd, sThisType;
2620                        for ( var i=0 ; i<aData.length ; i++ )
2621                        {
2622                                nTd = document.createElement('td');
2623                               
2624                                /* Allow null data (from a data array) - simply deal with it as a blank string */
2625                                if ( aData[i] === null )
2626                                {
2627                                        aData[i] = '';
2628                                }
2629                               
2630                                if ( typeof oSettings.aoColumns[i].fnRender == 'function' )
2631                                {
2632                                        var sRendered = oSettings.aoColumns[i].fnRender( {
2633                                                        "iDataRow": iThisIndex,
2634                                                        "iDataColumn": i,
2635                                                        "aData": aData,
2636                                                        "oSettings": oSettings
2637                                                } );
2638                                        nTd.innerHTML = sRendered;
2639                                        if ( oSettings.aoColumns[i].bUseRendered )
2640                                        {
2641                                                /* Use the rendered data for filtering/sorting */
2642                                                oSettings.aoData[iThisIndex]._aData[i] = sRendered;
2643                                        }
2644                                }
2645                                else
2646                                {
2647                                        nTd.innerHTML = aData[i];
2648                                }
2649                               
2650                                /* Cast everything as a string - so we can treat everything equally when sorting */
2651                                if ( typeof aData[i] != 'string' )
2652                                {
2653                                        aData[i] += "";
2654                                }
2655                                aData[i] = $.trim(aData[i]);
2656                               
2657                                /* Add user defined class */
2658                                if ( oSettings.aoColumns[i].sClass !== null )
2659                                {
2660                                        nTd.className = oSettings.aoColumns[i].sClass;
2661                                }
2662                               
2663                                /* See if we should auto-detect the column type */
2664                                if ( oSettings.aoColumns[i]._bAutoType && oSettings.aoColumns[i].sType != 'string' )
2665                                {
2666                                        /* Attempt to auto detect the type - same as _fnGatherData() */
2667                                        sThisType = _fnDetectType( oSettings.aoData[iThisIndex]._aData[i] );
2668                                        if ( oSettings.aoColumns[i].sType === null )
2669                                        {
2670                                                oSettings.aoColumns[i].sType = sThisType;
2671                                        }
2672                                        else if ( oSettings.aoColumns[i].sType != sThisType )
2673                                        {
2674                                                /* String is always the 'fallback' option */
2675                                                oSettings.aoColumns[i].sType = 'string';
2676                                        }
2677                                }
2678                                       
2679                                if ( oSettings.aoColumns[i].bVisible )
2680                                {
2681                                        oSettings.aoData[iThisIndex].nTr.appendChild( nTd );
2682                                        oSettings.aoData[iThisIndex]._anHidden[i] = null;
2683                                }
2684                                else
2685                                {
2686                                        oSettings.aoData[iThisIndex]._anHidden[i] = nTd;
2687                                }
2688                        }
2689                       
2690                        /* Add to the display array */
2691                        oSettings.aiDisplayMaster.push( iThisIndex );
2692                        return iThisIndex;
2693                }
2694               
2695                /*
2696                 * Function: _fnGatherData
2697                 * Purpose:  Read in the data from the target table from the DOM
2698                 * Returns:  -
2699                 * Inputs:   object:oSettings - dataTables settings object
2700                 * Notes:    This is a optimised version of _fnAddData (more or less) for reading information
2701                 *   from the DOM. The basic actions must be identical in the two functions.
2702                 */
2703                function _fnGatherData( oSettings )
2704                {
2705                        var iLoop, i, iLen, j, jLen, jInner,
2706                                nTds, nTrs, nTd, aLocalData, iThisIndex,
2707                                iRow, iRows, iColumn, iColumns;
2708                       
2709                        /*
2710                         * Process by row first
2711                         * Add the data object for the whole table - storing the tr node. Note - no point in getting
2712                         * DOM based data if we are going to go and replace it with Ajax source data.
2713                         */
2714                        if ( oSettings.sAjaxSource === null )
2715                        {
2716                                nTrs = oSettings.nTBody.childNodes;
2717                                for ( i=0, iLen=nTrs.length ; i<iLen ; i++ )
2718                                {
2719                                        if ( nTrs[i].nodeName.toUpperCase() == "TR" )
2720                                        {
2721                                                iThisIndex = oSettings.aoData.length;
2722                                                oSettings.aoData.push( {
2723                                                        "nTr": nTrs[i],
2724                                                        "_iId": oSettings.iNextId++,
2725                                                        "_aData": [],
2726                                                        "_anHidden": [],
2727                                                        "_sRowStripe": ''
2728                                                } );
2729                                               
2730                                                oSettings.aiDisplayMaster.push( iThisIndex );
2731                                               
2732                                                aLocalData = oSettings.aoData[iThisIndex]._aData;
2733                                                nTds = nTrs[i].childNodes;
2734                                                jInner = 0;
2735                                               
2736                                                for ( j=0, jLen=nTds.length ; j<jLen ; j++ )
2737                                                {
2738                                                        if ( nTds[j].nodeName.toUpperCase() == "TD" )
2739                                                        {
2740                                                                aLocalData[jInner] = $.trim(nTds[j].innerHTML);
2741                                                                jInner++;
2742                                                        }
2743                                                }
2744                                        }
2745                                }
2746                        }
2747                       
2748                        /* Gather in the TD elements of the Table - note that this is basically the same as
2749                         * fnGetTdNodes, but that function takes account of hidden columns, which we haven't yet
2750                         * setup!
2751                         */
2752                        nTrs = _fnGetTrNodes( oSettings );
2753                        nTds = [];
2754                        for ( i=0, iLen=nTrs.length ; i<iLen ; i++ )
2755                        {
2756                                for ( j=0, jLen=nTrs[i].childNodes.length ; j<jLen ; j++ )
2757                                {
2758                                        nTd = nTrs[i].childNodes[j];
2759                                        if ( nTd.nodeName.toUpperCase() == "TD" )
2760                                        {
2761                                                nTds.push( nTd );
2762                                        }
2763                                }
2764                        }
2765                       
2766                        /* Sanity check */
2767                        if ( nTds.length != nTrs.length * oSettings.aoColumns.length )
2768                        {
2769                                _fnLog( oSettings, 1, "Unexpected number of TD elements. Expected "+
2770                                        (nTrs.length * oSettings.aoColumns.length)+" and got "+nTds.length+". DataTables does "+
2771                                        "not support rowspan / colspan in the table body, and there must be one cell for each "+
2772                                        "row/column combination." );
2773                        }
2774                       
2775                        /* Now process by column */
2776                        for ( iColumn=0, iColumns=oSettings.aoColumns.length ; iColumn<iColumns ; iColumn++ )
2777                        {
2778                                /* Get the title of the column - unless there is a user set one */
2779                                if ( oSettings.aoColumns[iColumn].sTitle === null )
2780                                {
2781                                        oSettings.aoColumns[iColumn].sTitle = oSettings.aoColumns[iColumn].nTh.innerHTML;
2782                                }
2783                               
2784                                var
2785                                        bAutoType = oSettings.aoColumns[iColumn]._bAutoType,
2786                                        bRender = typeof oSettings.aoColumns[iColumn].fnRender == 'function',
2787                                        bClass = oSettings.aoColumns[iColumn].sClass !== null,
2788                                        bVisible = oSettings.aoColumns[iColumn].bVisible,
2789                                        nCell, sThisType, sRendered;
2790                               
2791                                /* A single loop to rule them all (and be more efficient) */
2792                                if ( bAutoType || bRender || bClass || !bVisible )
2793                                {
2794                                        for ( iRow=0, iRows=oSettings.aoData.length ; iRow<iRows ; iRow++ )
2795                                        {
2796                                                nCell = nTds[ (iRow*iColumns) + iColumn ];
2797                                               
2798                                                /* Type detection */
2799                                                if ( bAutoType )
2800                                                {
2801                                                        if ( oSettings.aoColumns[iColumn].sType != 'string' )
2802                                                        {
2803                                                                sThisType = _fnDetectType( oSettings.aoData[iRow]._aData[iColumn] );
2804                                                                if ( oSettings.aoColumns[iColumn].sType === null )
2805                                                                {
2806                                                                        oSettings.aoColumns[iColumn].sType = sThisType;
2807                                                                }
2808                                                                else if ( oSettings.aoColumns[iColumn].sType != sThisType )
2809                                                                {
2810                                                                        /* String is always the 'fallback' option */
2811                                                                        oSettings.aoColumns[iColumn].sType = 'string';
2812                                                                }
2813                                                        }
2814                                                }
2815                                               
2816                                                /* Rendering */
2817                                                if ( bRender )
2818                                                {
2819                                                        sRendered = oSettings.aoColumns[iColumn].fnRender( {
2820                                                                        "iDataRow": iRow,
2821                                                                        "iDataColumn": iColumn,
2822                                                                        "aData": oSettings.aoData[iRow]._aData,
2823                                                                        "oSettings": oSettings
2824                                                                } );
2825                                                        nCell.innerHTML = sRendered;
2826                                                        if ( oSettings.aoColumns[iColumn].bUseRendered )
2827                                                        {
2828                                                                /* Use the rendered data for filtering/sorting */
2829                                                                oSettings.aoData[iRow]._aData[iColumn] = sRendered;
2830                                                        }
2831                                                }
2832                                               
2833                                                /* Classes */
2834                                                if ( bClass )
2835                                                {
2836                                                        nCell.className += ' '+oSettings.aoColumns[iColumn].sClass;
2837                                                }
2838                                               
2839                                                /* Column visability */
2840                                                if ( !bVisible )
2841                                                {
2842                                                        oSettings.aoData[iRow]._anHidden[iColumn] = nCell;
2843                                                        nCell.parentNode.removeChild( nCell );
2844                                                }
2845                                                else
2846                                                {
2847                                                        oSettings.aoData[iRow]._anHidden[iColumn] = null;
2848                                                }
2849                                        }
2850                                }
2851                        }
2852                }
2853               
2854               
2855               
2856                /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2857                 * Section - Drawing functions
2858                 */
2859               
2860                /*
2861                 * Function: _fnDrawHead
2862                 * Purpose:  Create the HTML header for the table
2863                 * Returns:  -
2864                 * Inputs:   object:oSettings - dataTables settings object
2865                 */
2866                function _fnDrawHead( oSettings )
2867                {
2868                        var i, nTh, iLen, j, jLen;
2869                        var anTr = oSettings.nTHead.getElementsByTagName('tr');
2870                        var iThs = oSettings.nTHead.getElementsByTagName('th').length;
2871                        var iCorrector = 0;
2872                        var jqChildren;
2873                       
2874                        /* If there is a header in place - then use it - otherwise it's going to get nuked... */
2875                        if ( iThs !== 0 )
2876                        {
2877                                /* We've got a thead from the DOM, so remove hidden columns and apply width to vis cols */
2878                                for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
2879                                {
2880                                        nTh = oSettings.aoColumns[i].nTh;
2881                                       
2882                                        if ( oSettings.aoColumns[i].sClass !== null )
2883                                        {
2884                                                $(nTh).addClass( oSettings.aoColumns[i].sClass );
2885                                        }
2886                                       
2887                                        /* Cache and remove (if needed) any extra elements for this column in the header */
2888                                        for ( j=1, jLen=anTr.length ; j<jLen ; j++ )
2889                                        {
2890                                                jqChildren = $(anTr[j]).children();
2891                                                oSettings.aoColumns[i].anThExtra.push( jqChildren[i-iCorrector] );
2892                                                if ( !oSettings.aoColumns[i].bVisible )
2893                                                {
2894                                                        anTr[j].removeChild( jqChildren[i-iCorrector] );
2895                                                }
2896                                        }
2897                                       
2898                                        if ( oSettings.aoColumns[i].bVisible )
2899                                        {
2900                                                /* Set the title of the column if it is user defined (not what was auto detected) */
2901                                                if ( oSettings.aoColumns[i].sTitle != nTh.innerHTML )
2902                                                {
2903                                                        nTh.innerHTML = oSettings.aoColumns[i].sTitle;
2904                                                }
2905                                        }
2906                                        else
2907                                        {
2908                                                nTh.parentNode.removeChild( nTh );
2909                                                iCorrector++;
2910                                        }
2911                                }
2912                        }
2913                        else
2914                        {
2915                                /* We don't have a header in the DOM - so we are going to have to create one */
2916                                var nTr = document.createElement( "tr" );
2917                               
2918                                for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
2919                                {
2920                                        nTh = oSettings.aoColumns[i].nTh;
2921                                        nTh.innerHTML = oSettings.aoColumns[i].sTitle;
2922                                       
2923                                        if ( oSettings.aoColumns[i].sClass !== null )
2924                                        {
2925                                                $(nTh).addClass( oSettings.aoColumns[i].sClass );
2926                                        }
2927                                       
2928                                        if ( oSettings.aoColumns[i].bVisible )
2929                                        {
2930                                                nTr.appendChild( nTh );
2931                                        }
2932                                }
2933                                $(oSettings.nTHead).html( '' )[0].appendChild( nTr );
2934                        }
2935                       
2936                        /* Add the extra markup needed by jQuery UI's themes */
2937                        if ( oSettings.bJUI )
2938                        {
2939                                for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
2940                                {
2941                                        nTh = oSettings.aoColumns[i].nTh;
2942                                       
2943                                        var nDiv = document.createElement('div');
2944                                        nDiv.className = oSettings.oClasses.sSortJUIWrapper;
2945                                        $(nTh).contents().appendTo(nDiv);
2946                                       
2947                                        nDiv.appendChild( document.createElement('span') );
2948                                        nTh.appendChild( nDiv );
2949                                }
2950                        }
2951                       
2952                        /* Add sort listener */
2953                        var fnNoSelect = function (e) {
2954                                this.onselectstart = function() { return false; };
2955                                return false;
2956                        };
2957                       
2958                        if ( oSettings.oFeatures.bSort )
2959                        {
2960                                for ( i=0 ; i<oSettings.aoColumns.length ; i++ )
2961                                {
2962                                        if ( oSettings.aoColumns[i].bSortable !== false )
2963                                        {
2964                                                _fnSortAttachListener( oSettings, oSettings.aoColumns[i].nTh, i );
2965                                               
2966                                                /* Take the brutal approach to cancelling text selection in header */
2967                                                $(oSettings.aoColumns[i].nTh).bind( 'mousedown.DT', fnNoSelect );
2968                                        }
2969                                        else
2970                                        {
2971                                                $(oSettings.aoColumns[i].nTh).addClass( oSettings.oClasses.sSortableNone );
2972                                        }
2973                                }
2974                        }
2975                       
2976                        /* Cache the footer elements */
2977                        if ( oSettings.nTFoot !== null )
2978                        {
2979                                iCorrector = 0;
2980                                anTr = oSettings.nTFoot.getElementsByTagName('tr');
2981                                var nTfs = anTr[0].getElementsByTagName('th');
2982                               
2983                                for ( i=0, iLen=nTfs.length ; i<iLen ; i++ )
2984                                {
2985                                        if ( typeof oSettings.aoColumns[i] != 'undefined' )
2986                                        {
2987                                                oSettings.aoColumns[i].nTf = nTfs[i-iCorrector];
2988                                               
2989                                                if ( oSettings.oClasses.sFooterTH !== "" )
2990                                                {
2991                                                        oSettings.aoColumns[i].nTf.className += " "+oSettings.oClasses.sFooterTH;
2992                                                }
2993                                               
2994                                                /* Deal with any extra elements for this column from the footer */
2995                                                for ( j=1, jLen=anTr.length ; j<jLen ; j++ )
2996                                                {
2997                                                        jqChildren = $(anTr[j]).children();
2998                                                        oSettings.aoColumns[i].anTfExtra.push( jqChildren[i-iCorrector] );
2999                                                        if ( !oSettings.aoColumns[i].bVisible )
3000                                                        {
3001                                                                anTr[j].removeChild( jqChildren[i-iCorrector] );
3002                                                        }
3003                                                }
3004                                               
3005                                                if ( !oSettings.aoColumns[i].bVisible )
3006                                                {
3007                                                        nTfs[i-iCorrector].parentNode.removeChild( nTfs[i-iCorrector] );
3008                                                        iCorrector++;
3009                                                }
3010                                        }
3011                                }
3012                        }
3013                }
3014               
3015                /*
3016                 * Function: _fnDraw
3017                 * Purpose:  Insert the required TR nodes into the table for display
3018                 * Returns:  -
3019                 * Inputs:   object:oSettings - dataTables settings object
3020                 */
3021                function _fnDraw( oSettings )
3022                {
3023                        var i, iLen;
3024                        var anRows = [];
3025                        var iRowCount = 0;
3026                        var bRowError = false;
3027                        var iStrips = oSettings.asStripClasses.length;
3028                        var iOpenRows = oSettings.aoOpenRows.length;
3029                       
3030                        oSettings.bDrawing = true;
3031                       
3032                        /* Check and see if we have an initial draw position from state saving */
3033                        if ( typeof oSettings.iInitDisplayStart != 'undefined' && oSettings.iInitDisplayStart != -1 )
3034                        {
3035                                if ( oSettings.oFeatures.bServerSide )
3036                                {
3037                                        oSettings._iDisplayStart = oSettings.iInitDisplayStart;
3038                                }
3039                                else
3040                                {
3041                                        oSettings._iDisplayStart = (oSettings.iInitDisplayStart >= oSettings.fnRecordsDisplay()) ?
3042                                                0 : oSettings.iInitDisplayStart;
3043                                }
3044                                oSettings.iInitDisplayStart = -1;
3045                                _fnCalculateEnd( oSettings );
3046                        }
3047                       
3048                        /* If we are dealing with Ajax - do it here */
3049                        if ( !oSettings.bDestroying && oSettings.oFeatures.bServerSide &&
3050                             !_fnAjaxUpdate( oSettings ) )
3051                        {
3052                                return;
3053                        }
3054                        else if ( !oSettings.oFeatures.bServerSide )
3055                        {
3056                                oSettings.iDraw++;
3057                        }
3058                       
3059                        if ( oSettings.aiDisplay.length !== 0 )
3060                        {
3061                                var iStart = oSettings._iDisplayStart;
3062                                var iEnd = oSettings._iDisplayEnd;
3063                               
3064                                if ( oSettings.oFeatures.bServerSide )
3065                                {
3066                                        iStart = 0;
3067                                        iEnd = oSettings.aoData.length;
3068                                }
3069                               
3070                                for ( var j=iStart ; j<iEnd ; j++ )
3071                                {
3072                                        var aoData = oSettings.aoData[ oSettings.aiDisplay[j] ];
3073                                        var nRow = aoData.nTr;
3074                                       
3075                                        /* Remove the old stripping classes and then add the new one */
3076                                        if ( iStrips !== 0 )
3077                                        {
3078                                                var sStrip = oSettings.asStripClasses[ iRowCount % iStrips ];
3079                                                if ( aoData._sRowStripe != sStrip )
3080                                                {
3081                                                        $(nRow).removeClass( aoData._sRowStripe ).addClass( sStrip );
3082                                                        aoData._sRowStripe = sStrip;
3083                                                }
3084                                        }
3085                                       
3086                                        /* Custom row callback function - might want to manipule the row */
3087                                        if ( typeof oSettings.fnRowCallback == "function" )
3088                                        {
3089                                                nRow = oSettings.fnRowCallback.call( oSettings.oInstance, nRow,
3090                                                        oSettings.aoData[ oSettings.aiDisplay[j] ]._aData, iRowCount, j );
3091                                                if ( !nRow && !bRowError )
3092                                                {
3093                                                        _fnLog( oSettings, 0, "A node was not returned by fnRowCallback" );
3094                                                        bRowError = true;
3095                                                }
3096                                        }
3097                                       
3098                                        anRows.push( nRow );
3099                                        iRowCount++;
3100                                       
3101                                        /* If there is an open row - and it is attached to this parent - attach it on redraw */
3102                                        if ( iOpenRows !== 0 )
3103                                        {
3104                                                for ( var k=0 ; k<iOpenRows ; k++ )
3105                                                {
3106                                                        if ( nRow == oSettings.aoOpenRows[k].nParent )
3107                                                        {
3108                                                                anRows.push( oSettings.aoOpenRows[k].nTr );
3109                                                        }
3110                                                }
3111                                        }
3112                                }
3113                        }
3114                        else
3115                        {
3116                                /* Table is empty - create a row with an empty message in it */
3117                                anRows[ 0 ] = document.createElement( 'tr' );
3118                               
3119                                if ( typeof oSettings.asStripClasses[0] != 'undefined' )
3120                                {
3121                                        anRows[ 0 ].className = oSettings.asStripClasses[0];
3122                                }
3123                               
3124                                var nTd = document.createElement( 'td' );
3125                                nTd.setAttribute( 'valign', "top" );
3126                                nTd.colSpan = _fnVisbleColumns( oSettings );
3127                                nTd.className = oSettings.oClasses.sRowEmpty;
3128                                if ( typeof oSettings.oLanguage.sEmptyTable != 'undefined' &&
3129                                     oSettings.fnRecordsTotal() === 0 )
3130                                {
3131                                        nTd.innerHTML = oSettings.oLanguage.sEmptyTable;
3132                                }
3133                                else
3134                                {
3135                                        nTd.innerHTML = oSettings.oLanguage.sZeroRecords.replace(
3136                                                '_MAX_', oSettings.fnFormatNumber(oSettings.fnRecordsTotal()) );
3137                                }
3138                               
3139                                anRows[ iRowCount ].appendChild( nTd );
3140                        }
3141                       
3142                        /* Callback the header and footer custom funcation if there is one */
3143                        if ( typeof oSettings.fnHeaderCallback == 'function' )
3144                        {
3145                                oSettings.fnHeaderCallback.call( oSettings.oInstance, $('>tr', oSettings.nTHead)[0],
3146                                        _fnGetDataMaster( oSettings ), oSettings._iDisplayStart, oSettings.fnDisplayEnd(),
3147                                        oSettings.aiDisplay );
3148                        }
3149                       
3150                        if ( typeof oSettings.fnFooterCallback == 'function' )
3151                        {
3152                                oSettings.fnFooterCallback.call( oSettings.oInstance, $('>tr', oSettings.nTFoot)[0],
3153                                        _fnGetDataMaster( oSettings ), oSettings._iDisplayStart, oSettings.fnDisplayEnd(),
3154                                        oSettings.aiDisplay );
3155                        }
3156                       
3157                        /*
3158                         * Need to remove any old row from the display - note we can't just empty the tbody using
3159                         * $().html('') since this will unbind the jQuery event handlers (even although the node
3160                         * still exists!) - equally we can't use innerHTML, since IE throws an exception.
3161                         */
3162                        var
3163                                nAddFrag = document.createDocumentFragment(),
3164                                nRemoveFrag = document.createDocumentFragment(),
3165                                nBodyPar, nTrs;
3166                       
3167                        if ( oSettings.nTBody )
3168                        {
3169                                nBodyPar = oSettings.nTBody.parentNode;
3170                                nRemoveFrag.appendChild( oSettings.nTBody );
3171                               
3172                                /* When doing infinite scrolling, only remove child rows when sorting, filtering or start
3173                                 * up. When not infinite scroll, always do it.
3174                                 */
3175                                if ( !oSettings.oScroll.bInfinite || !oSettings._bInitComplete ||
3176                                        oSettings.bSorted || oSettings.bFiltered )
3177                                {
3178                                        nTrs = oSettings.nTBody.childNodes;
3179                                        for ( i=nTrs.length-1 ; i>=0 ; i-- )
3180                                        {
3181                                                nTrs[i].parentNode.removeChild( nTrs[i] );
3182                                        }
3183                                }
3184                               
3185                                /* Put the draw table into the dom */
3186                                for ( i=0, iLen=anRows.length ; i<iLen ; i++ )
3187                                {
3188                                        nAddFrag.appendChild( anRows[i] );
3189                                }
3190                               
3191                                oSettings.nTBody.appendChild( nAddFrag );
3192                                if ( nBodyPar !== null )
3193                                {
3194                                        nBodyPar.appendChild( oSettings.nTBody );
3195                                }
3196                        }
3197                       
3198                        /* Call all required callback functions for the end of a draw */
3199                        for ( i=oSettings.aoDrawCallback.length-1 ; i>=0 ; i-- )
3200                        {
3201                                oSettings.aoDrawCallback[i].fn.call( oSettings.oInstance, oSettings );
3202                        }
3203                       
3204                        /* Draw is complete, sorting and filtering must be as well */
3205                        oSettings.bSorted = false;
3206                        oSettings.bFiltered = false;
3207                        oSettings.bDrawing = false;
3208                       
3209                        if ( oSettings.oFeatures.bServerSide )
3210                        {
3211                                _fnProcessingDisplay( oSettings, false );
3212                                if ( typeof oSettings._bInitComplete == 'undefined' )
3213                                {
3214                                        _fnInitComplete( oSettings );
3215                                }
3216                        }
3217                }
3218               
3219                /*
3220                 * Function: _fnReDraw
3221                 * Purpose:  Redraw the table - taking account of the various features which are enabled
3222                 * Returns:  -
3223                 * Inputs:   object:oSettings - dataTables settings object
3224                 */
3225                function _fnReDraw( oSettings )
3226                {
3227                        if ( oSettings.oFeatures.bSort )
3228                        {
3229                                /* Sorting will refilter and draw for us */
3230                                _fnSort( oSettings, oSettings.oPreviousSearch );
3231                        }
3232                        else if ( oSettings.oFeatures.bFilter )
3233                        {
3234                                /* Filtering will redraw for us */
3235                                _fnFilterComplete( oSettings, oSettings.oPreviousSearch );
3236                        }
3237                        else
3238                        {
3239                                _fnCalculateEnd( oSettings );
3240                                _fnDraw( oSettings );
3241                        }
3242                }
3243               
3244                /*
3245                 * Function: _fnAjaxUpdate
3246                 * Purpose:  Update the table using an Ajax call
3247                 * Returns:  bool: block the table drawing or not
3248                 * Inputs:   object:oSettings - dataTables settings object
3249                 */
3250                function _fnAjaxUpdate( oSettings )
3251                {
3252                        if ( oSettings.bAjaxDataGet )
3253                        {
3254                                _fnProcessingDisplay( oSettings, true );
3255                                var iColumns = oSettings.aoColumns.length;
3256                                var aoData = [];
3257                                var i;
3258                               
3259                                /* Paging and general */
3260                                oSettings.iDraw++;
3261                                aoData.push( { "name": "sEcho",          "value": oSettings.iDraw } );
3262                                aoData.push( { "name": "iColumns",       "value": iColumns } );
3263                                aoData.push( { "name": "sColumns",       "value": _fnColumnOrdering(oSettings) } );
3264                                aoData.push( { "name": "iDisplayStart",  "value": oSettings._iDisplayStart } );
3265                                aoData.push( { "name": "iDisplayLength", "value": oSettings.oFeatures.bPaginate !== false ?
3266                                        oSettings._iDisplayLength : -1 } );
3267                               
3268                                /* Filtering */
3269                                if ( oSettings.oFeatures.bFilter !== false )
3270                                {
3271                                        aoData.push( { "name": "sSearch", "value": oSettings.oPreviousSearch.sSearch } );
3272                                        aoData.push( { "name": "bRegex",  "value": oSettings.oPreviousSearch.bRegex } );
3273                                        for ( i=0 ; i<iColumns ; i++ )
3274                                        {
3275                                                aoData.push( { "name": "sSearch_"+i,     "value": oSettings.aoPreSearchCols[i].sSearch } );
3276                                                aoData.push( { "name": "bRegex_"+i,      "value": oSettings.aoPreSearchCols[i].bRegex } );
3277                                                aoData.push( { "name": "bSearchable_"+i, "value": oSettings.aoColumns[i].bSearchable } );
3278                                        }
3279                                }
3280                               
3281                                /* Sorting */
3282                                if ( oSettings.oFeatures.bSort !== false )
3283                                {
3284                                        var iFixed = oSettings.aaSortingFixed !== null ? oSettings.aaSortingFixed.length : 0;
3285                                        var iUser = oSettings.aaSorting.length;
3286                                        aoData.push( { "name": "iSortingCols",   "value": iFixed+iUser } );
3287                                        for ( i=0 ; i<iFixed ; i++ )
3288                                        {
3289                                                aoData.push( { "name": "iSortCol_"+i,  "value": oSettings.aaSortingFixed[i][0] } );
3290                                                aoData.push( { "name": "sSortDir_"+i,  "value": oSettings.aaSortingFixed[i][1] } );
3291                                        }
3292                                       
3293                                        for ( i=0 ; i<iUser ; i++ )
3294                                        {
3295                                                aoData.push( { "name": "iSortCol_"+(i+iFixed),  "value": oSettings.aaSorting[i][0] } );
3296                                                aoData.push( { "name": "sSortDir_"+(i+iFixed),  "value": oSettings.aaSorting[i][1] } );
3297                                        }
3298                                       
3299                                        for ( i=0 ; i<iColumns ; i++ )
3300                                        {
3301                                                aoData.push( { "name": "bSortable_"+i,  "value": oSettings.aoColumns[i].bSortable } );
3302                                        }
3303                                }
3304                               
3305                                oSettings.fnServerData.call( oSettings.oInstance, oSettings.sAjaxSource, aoData,
3306                                        function(json) {
3307                                                _fnAjaxUpdateDraw( oSettings, json );
3308                                        } );
3309                                return false;
3310                        }
3311                        else
3312                        {
3313                                return true;
3314                        }
3315                }
3316               
3317                /*
3318                 * Function: _fnAjaxUpdateDraw
3319                 * Purpose:  Data the data from the server (nuking the old) and redraw the table
3320                 * Returns:  -
3321                 * Inputs:   object:oSettings - dataTables settings object
3322                 *           object:json - json data return from the server.
3323                 *             The following must be defined:
3324                 *               iTotalRecords, iTotalDisplayRecords, aaData
3325                 *             The following may be defined:
3326                 *               sColumns
3327                 */
3328                function _fnAjaxUpdateDraw ( oSettings, json )
3329                {
3330                        if ( typeof json.sEcho != 'undefined' )
3331                        {
3332                                /* Protect against old returns over-writing a new one. Possible when you get
3333                                 * very fast interaction, and later queires are completed much faster
3334                                 */
3335                                if ( json.sEcho*1 < oSettings.iDraw )
3336                                {
3337                                        return;
3338                                }
3339                                else
3340                                {
3341                                        oSettings.iDraw = json.sEcho * 1;
3342                                }
3343                        }
3344                       
3345                        if ( !oSettings.oScroll.bInfinite ||
3346                                   (oSettings.oScroll.bInfinite && (oSettings.bSorted || oSettings.bFiltered)) )
3347                        {
3348                                _fnClearTable( oSettings );
3349                        }
3350                        oSettings._iRecordsTotal = json.iTotalRecords;
3351                        oSettings._iRecordsDisplay = json.iTotalDisplayRecords;
3352                       
3353                        /* Determine if reordering is required */
3354                        var sOrdering = _fnColumnOrdering(oSettings);
3355                        var bReOrder = (typeof json.sColumns != 'undefined' && sOrdering !== "" && json.sColumns != sOrdering );
3356                        if ( bReOrder )
3357                        {
3358                                var aiIndex = _fnReOrderIndex( oSettings, json.sColumns );
3359                        }
3360                       
3361                        for ( var i=0, iLen=json.aaData.length ; i<iLen ; i++ )
3362                        {
3363                                if ( bReOrder )
3364                                {
3365                                        /* If we need to re-order, then create a new array with the correct order and add it */
3366                                        var aData = [];
3367                                        for ( var j=0, jLen=oSettings.aoColumns.length ; j<jLen ; j++ )
3368                                        {
3369                                                aData.push( json.aaData[i][ aiIndex[j] ] );
3370                                        }
3371                                        _fnAddData( oSettings, aData );
3372                                }
3373                                else
3374                                {
3375                                        /* No re-order required, sever got it "right" - just straight add */
3376                                        _fnAddData( oSettings, json.aaData[i] );
3377                                }
3378                        }
3379                        oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
3380                       
3381                        oSettings.bAjaxDataGet = false;
3382                        _fnDraw( oSettings );
3383                        oSettings.bAjaxDataGet = true;
3384                        _fnProcessingDisplay( oSettings, false );
3385                }
3386               
3387               
3388                /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
3389                 * Section - Options (features) HTML
3390                 */
3391               
3392                /*
3393                 * Function: _fnAddOptionsHtml
3394                 * Purpose:  Add the options to the page HTML for the table
3395                 * Returns:  -
3396                 * Inputs:   object:oSettings - dataTables settings object
3397                 */
3398                function _fnAddOptionsHtml ( oSettings )
3399                {
3400                        /*
3401                         * Create a temporary, empty, div which we can later on replace with what we have generated
3402                         * we do it this way to rendering the 'options' html offline - speed :-)
3403                         */
3404                        var nHolding = document.createElement( 'div' );
3405                        oSettings.nTable.parentNode.insertBefore( nHolding, oSettings.nTable );
3406                       
3407                        /*
3408                         * All DataTables are wrapped in a div - this is not currently optional - backwards
3409                         * compatability. It can be removed if you don't want it.
3410                         */
3411                        oSettings.nTableWrapper = document.createElement( 'div' );
3412                        oSettings.nTableWrapper.className = oSettings.oClasses.sWrapper;
3413                        if ( oSettings.sTableId !== '' )
3414                        {
3415                                oSettings.nTableWrapper.setAttribute( 'id', oSettings.sTableId+'_wrapper' );
3416                        }
3417                       
3418                        /* Track where we want to insert the option */
3419                        var nInsertNode = oSettings.nTableWrapper;
3420                       
3421                        /* Loop over the user set positioning and place the elements as needed */
3422                        var aDom = oSettings.sDom.split('');
3423                        var nTmp, iPushFeature, cOption, nNewNode, cNext, sAttr, j;
3424                        for ( var i=0 ; i<aDom.length ; i++ )
3425                        {
3426                                iPushFeature = 0;
3427                                cOption = aDom[i];
3428                               
3429                                if ( cOption == '<' )
3430                                {
3431                                        /* New container div */
3432                                        nNewNode = document.createElement( 'div' );
3433                                       
3434                                        /* Check to see if we should append an id and/or a class name to the container */
3435                                        cNext = aDom[i+1];
3436                                        if ( cNext == "'" || cNext == '"' )
3437                                        {
3438                                                sAttr = "";
3439                                                j = 2;
3440                                                while ( aDom[i+j] != cNext )
3441                                                {
3442                                                        sAttr += aDom[i+j];
3443                                                        j++;
3444                                                }
3445                                               
3446                                                /* Replace jQuery UI constants */
3447                                                if ( sAttr == "H" )
3448                                                {
3449                                                        sAttr = "fg-toolbar ui-toolbar ui-widget-header ui-corner-tl ui-corner-tr ui-helper-clearfix";
3450                                                }
3451                                                else if ( sAttr == "F" )
3452                                                {
3453                                                        sAttr = "fg-toolbar ui-toolbar ui-widget-header ui-corner-bl ui-corner-br ui-helper-clearfix";
3454                                                }
3455                                               
3456                                                /* The attribute can be in the format of "#id.class", "#id" or "class" This logic
3457                                                 * breaks the string into parts and applies them as needed
3458                                                 */
3459                                                if ( sAttr.indexOf('.') != -1 )
3460                                                {
3461                                                        var aSplit = sAttr.split('.');
3462                                                        nNewNode.setAttribute('id', aSplit[0].substr(1, aSplit[0].length-1) );
3463                                                        nNewNode.className = aSplit[1];
3464                                                }
3465                                                else if ( sAttr.charAt(0) == "#" )
3466                                                {
3467                                                        nNewNode.setAttribute('id', sAttr.substr(1, sAttr.length-1) );
3468                                                }
3469                                                else
3470                                                {
3471                                                        nNewNode.className = sAttr;
3472                                                }
3473                                               
3474                                                i += j; /* Move along the position array */
3475                                        }
3476                                       
3477                                        nInsertNode.appendChild( nNewNode );
3478                                        nInsertNode = nNewNode;
3479                                }
3480                                else if ( cOption == '>' )
3481                                {
3482                                        /* End container div */
3483                                        nInsertNode = nInsertNode.parentNode;
3484                                }
3485                                else if ( cOption == 'l' && oSettings.oFeatures.bPaginate && oSettings.oFeatures.bLengthChange )
3486                                {
3487                                        /* Length */
3488                                        nTmp = _fnFeatureHtmlLength( oSettings );
3489                                        iPushFeature = 1;
3490                                }
3491                                else if ( cOption == 'f' && oSettings.oFeatures.bFilter )
3492                                {
3493                                        /* Filter */
3494                                        nTmp = _fnFeatureHtmlFilter( oSettings );
3495                                        iPushFeature = 1;
3496                                }
3497                                else if ( cOption == 'r' && oSettings.oFeatures.bProcessing )
3498                                {
3499                                        /* pRocessing */
3500                                        nTmp = _fnFeatureHtmlProcessing( oSettings );
3501                                        iPushFeature = 1;
3502                                }
3503                                else if ( cOption == 't' )
3504                                {
3505                                        /* Table */
3506                                        nTmp = _fnFeatureHtmlTable( oSettings );
3507                                        iPushFeature = 1;
3508                                }
3509                                else if ( cOption ==  'i' && oSettings.oFeatures.bInfo )
3510                                {
3511                                        /* Info */
3512                                        nTmp = _fnFeatureHtmlInfo( oSettings );
3513                                        iPushFeature = 1;
3514                                }
3515                                else if ( cOption == 'p' && oSettings.oFeatures.bPaginate )
3516                                {
3517                                        /* Pagination */
3518                                        nTmp = _fnFeatureHtmlPaginate( oSettings );
3519                                        iPushFeature = 1;
3520                                }
3521                                else if ( _oExt.aoFeatures.length !== 0 )
3522                                {
3523                                        /* Plug-in features */
3524                                        var aoFeatures = _oExt.aoFeatures;
3525                                        for ( var k=0, kLen=aoFeatures.length ; k<kLen ; k++ )
3526                                        {
3527                                                if ( cOption == aoFeatures[k].cFeature )
3528                                                {
3529                                                        nTmp = aoFeatures[k].fnInit( oSettings );
3530                                                        if ( nTmp )
3531                                                        {
3532                                                                iPushFeature = 1;
3533                                                        }
3534                                                        break;
3535                                                }
3536                                        }
3537                                }
3538                               
3539                                /* Add to the 2D features array */
3540                                if ( iPushFeature == 1 && nTmp !== null )
3541                                {
3542                                        if ( typeof oSettings.aanFeatures[cOption] != 'object' )
3543                                        {
3544                                                oSettings.aanFeatures[cOption] = [];
3545                                        }
3546                                        oSettings.aanFeatures[cOption].push( nTmp );
3547                                        nInsertNode.appendChild( nTmp );
3548                                }
3549                        }
3550                       
3551                        /* Built our DOM structure - replace the holding div with what we want */
3552                        nHolding.parentNode.replaceChild( oSettings.nTableWrapper, nHolding );
3553                }
3554               
3555               
3556                /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
3557                 * Section - Feature: Filtering
3558                 */
3559               
3560                /*
3561                 * Function: _fnFeatureHtmlTable
3562                 * Purpose:  Add any control elements for the table - specifically scrolling
3563                 * Returns:  node: - Node to add to the DOM
3564                 * Inputs:   object:oSettings - dataTables settings object
3565                 */
3566                function _fnFeatureHtmlTable ( oSettings )
3567                {
3568                        /* Chack if scrolling is enabled or not - if not then leave the DOM unaltered */
3569                        if ( oSettings.oScroll.sX === "" && oSettings.oScroll.sY === "" )
3570                        {
3571                                return oSettings.nTable;
3572                        }
3573                       
3574                        /*
3575                         * The HTML structure that we want to generate in this function is:
3576                         *  div - nScroller
3577                         *    div - nScrollHead
3578                         *      div - nScrollHeadInner
3579                         *        table - nScrollHeadTable
3580                         *          thead - nThead
3581                         *    div - nScrollBody
3582                         *      table - oSettings.nTable
3583                         *        thead - nTheadSize
3584                         *        tbody - nTbody
3585                         *    div - nScrollFoot
3586                         *      div - nScrollFootInner
3587                         *        table - nScrollFootTable
3588                         *          tfoot - nTfoot
3589                         */
3590                        var
3591                                nScroller = document.createElement('div'),
3592                                nScrollHead = document.createElement('div'),
3593                                nScrollHeadInner = document.createElement('div'),
3594                                nScrollBody = document.createElement('div'),
3595                                nScrollFoot = document.createElement('div'),
3596                                nScrollFootInner = document.createElement('div'),
3597                                nScrollHeadTable = oSettings.nTable.cloneNode(false),
3598                                nScrollFootTable = oSettings.nTable.cloneNode(false),
3599                                nThead = oSettings.nTable.getElementsByTagName('thead')[0],
3600                                nTfoot = oSettings.nTable.getElementsByTagName('tfoot').length === 0 ? null :
3601                                        oSettings.nTable.getElementsByTagName('tfoot')[0],
3602                                oClasses = (typeof oInit.bJQueryUI != 'undefined' && oInit.bJQueryUI) ?
3603                                        _oExt.oJUIClasses : _oExt.oStdClasses;
3604                       
3605                        nScrollHead.appendChild( nScrollHeadInner );
3606                        nScrollFoot.appendChild( nScrollFootInner );
3607                        nScrollBody.appendChild( oSettings.nTable );
3608                        nScroller.appendChild( nScrollHead );
3609                        nScroller.appendChild( nScrollBody );
3610                        nScrollHeadInner.appendChild( nScrollHeadTable );
3611                        nScrollHeadTable.appendChild( nThead );
3612                        if ( nTfoot !== null )
3613                        {
3614                                nScroller.appendChild( nScrollFoot );
3615                                nScrollFootInner.appendChild( nScrollFootTable );
3616                                nScrollFootTable.appendChild( nTfoot );
3617                        }
3618                       
3619                        nScroller.className = oClasses.sScrollWrapper;
3620                        nScrollHead.className = oClasses.sScrollHead;
3621                        nScrollHeadInner.className = oClasses.sScrollHeadInner;
3622                        nScrollBody.className = oClasses.sScrollBody;
3623                        nScrollFoot.className = oClasses.sScrollFoot;
3624                        nScrollFootInner.className = oClasses.sScrollFootInner;
3625                       
3626                        if ( oSettings.oScroll.bAutoCss )
3627                        {
3628                                nScrollHead.style.overflow = "hidden";
3629                                nScrollHead.style.position = "relative";
3630                                nScrollFoot.style.overflow = "hidden";
3631                                nScrollBody.style.overflow = "auto";
3632                        }
3633                       
3634                        nScrollHead.style.border = "0";
3635                        nScrollHead.style.width = "100%";
3636                        nScrollFoot.style.border = "0";
3637                        nScrollHeadInner.style.width = "150%"; /* will be overwritten */
3638                       
3639                        /* Modify attributes to respect the clones */
3640                        nScrollHeadTable.removeAttribute('id');
3641                        nScrollHeadTable.style.marginLeft = "0";
3642                        oSettings.nTable.style.marginLeft = "0";
3643                        if ( nTfoot !== null )
3644                        {
3645                                nScrollFootTable.removeAttribute('id');
3646                                nScrollFootTable.style.marginLeft = "0";
3647                        }
3648                       
3649                        /* Move any caption elements from the body to the header */
3650                        var nCaptions = $('>caption', oSettings.nTable);
3651                        for ( var i=0, iLen=nCaptions.length ; i<iLen ; i++ )
3652                        {
3653                                nScrollHeadTable.appendChild( nCaptions[i] );
3654                        }
3655                       
3656                        /*
3657                         * Sizing
3658                         */
3659                        /* When xscrolling add the width and a scroller to move the header with the body */
3660                        if ( oSettings.oScroll.sX !== "" )
3661                        {
3662                                nScrollHead.style.width = _fnStringToCss( oSettings.oScroll.sX );
3663                                nScrollBody.style.width = _fnStringToCss( oSettings.oScroll.sX );
3664                               
3665                                if ( nTfoot !== null )
3666                                {
3667                                        nScrollFoot.style.width = _fnStringToCss( oSettings.oScroll.sX );       
3668                                }
3669                               
3670                                /* When the body is scrolled, then we also want to scroll the headers */
3671                                $(nScrollBody).scroll( function (e) {
3672                                        nScrollHead.scrollLeft = this.scrollLeft;
3673                                       
3674                                        if ( nTfoot !== null )
3675                                        {
3676                                                nScrollFoot.scrollLeft = this.scrollLeft;
3677                                        }
3678                                } );
3679                        }
3680                       
3681                        /* When yscrolling, add the height */
3682                        if ( oSettings.oScroll.sY !== "" )
3683                        {
3684                                nScrollBody.style.height = _fnStringToCss( oSettings.oScroll.sY );
3685                        }
3686                       
3687                        /* Redraw - align columns across the tables */
3688                        oSettings.aoDrawCallback.push( {
3689                                "fn": _fnScrollDraw,
3690                                "sName": "scrolling"
3691                        } );
3692                       
3693                        /* Infinite scrolling event handlers */
3694                        if ( oSettings.oScroll.bInfinite )
3695                        {
3696                                $(nScrollBody).scroll( function() {
3697                                        /* Use a blocker to stop scrolling from loading more data while other data is still loading */
3698                                        if ( !oSettings.bDrawing )
3699                                        {
3700                                                /* Check if we should load the next data set */
3701                                                if ( $(this).scrollTop() + $(this).height() >
3702                                                        $(oSettings.nTable).height() - oSettings.oScroll.iLoadGap )
3703                                                {
3704                                                        /* Only do the redraw if we have to - we might be at the end of the data */
3705                                                        if ( oSettings.fnDisplayEnd() < oSettings.fnRecordsDisplay() )
3706                                                        {
3707                                                                _fnPageChange( oSettings, 'next' );
3708                                                                _fnCalculateEnd( oSettings );
3709                                                                _fnDraw( oSettings );
3710                                                        }
3711                                                }
3712                                        }
3713                                } );
3714                        }
3715                       
3716                        oSettings.nScrollHead = nScrollHead;
3717                        oSettings.nScrollFoot = nScrollFoot;
3718                       
3719                        return nScroller;
3720                }
3721               
3722                /*
3723                 * Function: _fnScrollDraw
3724                 * Purpose:  Update the various tables for resizing
3725                 * Returns:  node: - Node to add to the DOM
3726                 * Inputs:   object:o - dataTables settings object
3727                 * Notes:    It's a bit of a pig this function, but basically the idea to:
3728                 *   1. Re-create the table inside the scrolling div
3729                 *   2. Take live measurements from the DOM
3730                 *   3. Apply the measurements
3731                 *   4. Clean up
3732                 */
3733                function _fnScrollDraw ( o )
3734                {
3735                        var
3736                                nScrollHeadInner = o.nScrollHead.getElementsByTagName('div')[0],
3737                                nScrollHeadTable = nScrollHeadInner.getElementsByTagName('table')[0],
3738                                nScrollBody = o.nTable.parentNode,
3739                                i, iLen, j, jLen, anHeadToSize, anHeadSizers, anFootSizers, anFootToSize, oStyle, iVis,
3740                                iWidth, aApplied=[], iSanityWidth;
3741                       
3742                        /*
3743                         * 1. Re-create the table inside the scrolling div
3744                         */
3745                       
3746                        /* Remove the old minimised thead and tfoot elements in the inner table */
3747                        var nTheadSize = o.nTable.getElementsByTagName('thead');
3748                        if ( nTheadSize.length > 0 )
3749                        {
3750                                o.nTable.removeChild( nTheadSize[0] );
3751                        }
3752                       
3753                        if ( o.nTFoot !== null )
3754                        {
3755                                /* Remove the old minimised footer element in the cloned header */
3756                                var nTfootSize = o.nTable.getElementsByTagName('tfoot');
3757                                if ( nTfootSize.length > 0 )
3758                                {
3759                                        o.nTable.removeChild( nTfootSize[0] );
3760                                }
3761                        }
3762                       
3763                        /* Clone the current header and footer elements and then place it into the inner table */
3764                        nTheadSize = o.nTHead.cloneNode(true);
3765                        o.nTable.insertBefore( nTheadSize, o.nTable.childNodes[0] );
3766                       
3767                        if ( o.nTFoot !== null )
3768                        {
3769                                nTfootSize = o.nTFoot.cloneNode(true);
3770                                o.nTable.insertBefore( nTfootSize, o.nTable.childNodes[1] );
3771                        }
3772                       
3773                        /*
3774                         * 2. Take live measurements from the DOM - do not alter the DOM itself!
3775                         */
3776                       
3777                        /* Remove old sizing and apply the calculated column widths
3778                         * Get the unique column headers in the newly created (cloned) header. We want to apply the
3779                         * calclated sizes to this header
3780                         */
3781                        var nThs = _fnGetUniqueThs( nTheadSize );
3782                        for ( i=0, iLen=nThs.length ; i<iLen ; i++ )
3783                        {
3784                                iVis = _fnVisibleToColumnIndex( o, i );
3785                                nThs[i].style.width = o.aoColumns[iVis].sWidth;
3786                        }
3787                       
3788                        if ( o.nTFoot !== null )
3789                        {
3790                                _fnApplyToChildren( function(n) {
3791                                        n.style.width = "";
3792                                }, nTfootSize.getElementsByTagName('tr') );
3793                        }
3794                       
3795                        /* Size the table as a whole */
3796                        iSanityWidth = $(o.nTable).outerWidth();
3797                        if ( o.oScroll.sX === "" )
3798                        {
3799                                /* No x scrolling */
3800                                o.nTable.style.width = "100%";
3801                               
3802                                /* I know this is rubbish - but IE7 will make the width of the table when 100% include
3803                                 * the scrollbar - which is shouldn't. This needs feature detection in future - to do
3804                                 */
3805                                if ( $.browser.msie && $.browser.version <= 7 )
3806                                {
3807                                        o.nTable.style.width = _fnStringToCss( $(o.nTable).outerWidth()-o.oScroll.iBarWidth );
3808                                }
3809                        }
3810                        else
3811                        {
3812                                if ( o.oScroll.sXInner !== "" )
3813                                {
3814                                        /* x scroll inner has been given - use it */
3815                                        o.nTable.style.width = _fnStringToCss(o.oScroll.sXInner);
3816                                }
3817                                else if ( iSanityWidth == $(nScrollBody).width() &&
3818                                   $(nScrollBody).height() < $(o.nTable).height() )
3819                                {
3820                                        /* There is y-scrolling - try to take account of the y scroll bar */
3821                                        o.nTable.style.width = _fnStringToCss( iSanityWidth-o.oScroll.iBarWidth );
3822                                        if ( $(o.nTable).outerWidth() > iSanityWidth-o.oScroll.iBarWidth )
3823                                        {
3824                                                /* Not possible to take account of it */
3825                                                o.nTable.style.width = _fnStringToCss( iSanityWidth );
3826                                        }
3827                                }
3828                                else
3829                                {
3830                                        /* All else fails */
3831                                        o.nTable.style.width = _fnStringToCss( iSanityWidth );
3832                                }
3833                        }
3834                       
3835                        /* Recalculate the sanity width - now that we've applied the required width, before it was
3836                         * a temporary variable. This is required because the column width calculation is done
3837                         * before this table DOM is created.
3838                         */
3839                        iSanityWidth = $(o.nTable).outerWidth();
3840                       
3841                        /* We want the hidden header to have zero height, so remove padding and borders. Then
3842                         * set the width based on the real headers
3843                         */
3844                        anHeadToSize = o.nTHead.getElementsByTagName('tr');
3845                        anHeadSizers = nTheadSize.getElementsByTagName('tr');
3846                       
3847                        _fnApplyToChildren( function(nSizer, nToSize) {
3848                                oStyle = nSizer.style;
3849                                oStyle.paddingTop = "0";
3850                                oStyle.paddingBottom = "0";
3851                                oStyle.borderTopWidth = "0";
3852                                oStyle.borderBottomWidth = "0";
3853                                oStyle.height = 0;
3854                               
3855                                iWidth = $(nSizer).width();
3856                                nToSize.style.width = _fnStringToCss( iWidth );
3857                                aApplied.push( iWidth );
3858                        }, anHeadSizers, anHeadToSize );
3859                        $(anHeadSizers).height(0);
3860                       
3861                        if ( o.nTFoot !== null )
3862                        {
3863                                /* Clone the current footer and then place it into the body table as a "hidden header" */
3864                                anFootSizers = nTfootSize.getElementsByTagName('tr');
3865                                anFootToSize = o.nTFoot.getElementsByTagName('tr');
3866                               
3867                                _fnApplyToChildren( function(nSizer, nToSize) {
3868                                        oStyle = nSizer.style;
3869                                        oStyle.paddingTop = "0";
3870                                        oStyle.paddingBottom = "0";
3871                                        oStyle.borderTopWidth = "0";
3872                                        oStyle.borderBottomWidth = "0";
3873                                        oStyle.height = 0;
3874                                       
3875                                        iWidth = $(nSizer).width();
3876                                        nToSize.style.width = _fnStringToCss( iWidth );
3877                                        aApplied.push( iWidth );
3878                                }, anFootSizers, anFootToSize );
3879                                $(anFootSizers).height(0);
3880                        }
3881                       
3882                        /*
3883                         * 3. Apply the measurements
3884                         */
3885                       
3886                        /* "Hide" the header and footer that we used for the sizing. We want to also fix their width
3887                         * to what they currently are
3888                         */
3889                        _fnApplyToChildren( function(nSizer) {
3890                                nSizer.innerHTML = "";
3891                                nSizer.style.width = _fnStringToCss( aApplied.shift() );
3892                        }, anHeadSizers );
3893                       
3894                        if ( o.nTFoot !== null )
3895                        {
3896                                _fnApplyToChildren( function(nSizer) {
3897                                        nSizer.innerHTML = "";
3898                                        nSizer.style.width = _fnStringToCss( aApplied.shift() );
3899                                }, anFootSizers );
3900                        }
3901                       
3902                        /* Sanity check that the table is of a sensible width. If not then we are going to get
3903                         * misalignment
3904                         */
3905                        if ( $(o.nTable).outerWidth() < iSanityWidth )
3906                        {
3907                                if ( o.oScroll.sX === "" )
3908                                {
3909                                        _fnLog( o, 1, "The table cannot fit into the current element which will cause column"+
3910                                                " misalignment. It is suggested that you enable x-scrolling or increase the width"+
3911                                                " the table has in which to be drawn" );
3912                                }
3913                                else if ( o.oScroll.sXInner !== "" )
3914                                {
3915                                        _fnLog( o, 1, "The table cannot fit into the current element which will cause column"+
3916                                                " misalignment. It is suggested that you increase the sScrollXInner property to"+
3917                                                " allow it to draw in a larger area, or simply remove that parameter to allow"+
3918                                                " automatic calculation" );
3919                                }
3920                        }
3921                       
3922                       
3923                        /*
3924                         * 4. Clean up
3925                         */
3926                       
3927                        if ( o.oScroll.sY === "" )
3928                        {
3929                                /* IE7< puts a vertical scrollbar in place (when it shouldn't be) due to subtracting
3930                                 * the scrollbar height from the visible display, rather than adding it on. We need to
3931                                 * set the height in order to sort this. Don't want to do it in any other browsers.
3932                                 */
3933                                if ( $.browser.msie && $.browser.version <= 7 )
3934                                {
3935                                        nScrollBody.style.height = _fnStringToCss( o.nTable.offsetHeight+o.oScroll.iBarWidth );
3936                                }
3937                        }
3938                       
3939                        if ( o.oScroll.sY !== "" && o.oScroll.bCollapse )
3940                        {
3941                                nScrollBody.style.height = _fnStringToCss( o.oScroll.sY );
3942                               
3943                                var iExtra = (o.oScroll.sX !== "" && o.nTable.offsetWidth > nScrollBody.offsetWidth) ?
3944                                        o.oScroll.iBarWidth : 0;
3945                                if ( o.nTable.offsetHeight < nScrollBody.offsetHeight )
3946                                {
3947                                        nScrollBody.style.height = _fnStringToCss( $(o.nTable).height()+iExtra );
3948                                }
3949                        }
3950                       
3951                        /* Finally set the width's of the header and footer tables */
3952                        var iOuterWidth = $(o.nTable).outerWidth();
3953                        nScrollHeadTable.style.width = _fnStringToCss( iOuterWidth );
3954                        nScrollHeadInner.style.width = _fnStringToCss( iOuterWidth+o.oScroll.iBarWidth );
3955                       
3956                        if ( o.nTFoot !== null )
3957                        {
3958                                var
3959                                        nScrollFootInner = o.nScrollFoot.getElementsByTagName('div')[0],
3960                                        nScrollFootTable = nScrollFootInner.getElementsByTagName('table')[0];
3961                               
3962                                nScrollFootInner.style.width = _fnStringToCss( o.nTable.offsetWidth+o.oScroll.iBarWidth );
3963                                nScrollFootTable.style.width = _fnStringToCss( o.nTable.offsetWidth );
3964                        }
3965                       
3966                        /* If sorting or filtering has occured, jump the scrolling back to the top */
3967                        if ( o.bSorted || o.bFiltered )
3968                        {
3969                                nScrollBody.scrollTop = 0;
3970                        }
3971                }
3972               
3973                /*
3974                 * Function: _fnAjustColumnSizing
3975                 * Purpose:  Ajust the table column widths for new data
3976                 * Returns:  -
3977                 * Inputs:   object:oSettings - dataTables settings object
3978                 * Notes:    You would probably want to do a redraw after calling this function!
3979                 */
3980                function _fnAjustColumnSizing ( oSettings )
3981                {
3982                        /* Not interested in doing column width calculation if autowidth is disabled */
3983                        if ( oSettings.oFeatures.bAutoWidth === false )
3984                        {
3985                                return false;
3986                        }
3987                       
3988                        _fnCalculateColumnWidths( oSettings );
3989                        for ( var i=0 , iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
3990                        {
3991                                oSettings.aoColumns[i].nTh.style.width = oSettings.aoColumns[i].sWidth;
3992                        }
3993                }
3994               
3995               
3996                /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
3997                 * Section - Feature: Filtering
3998                 */
3999               
4000                /*
4001                 * Function: _fnFeatureHtmlFilter
4002                 * Purpose:  Generate the node required for filtering text
4003                 * Returns:  node
4004                 * Inputs:   object:oSettings - dataTables settings object
4005                 */
4006                function _fnFeatureHtmlFilter ( oSettings )
4007                {
4008                        var nFilter = document.createElement( 'div' );
4009                        if ( oSettings.sTableId !== '' && typeof oSettings.aanFeatures.f == "undefined" )
4010                        {
4011                                nFilter.setAttribute( 'id', oSettings.sTableId+'_filter' );
4012                        }
4013                        nFilter.className = oSettings.oClasses.sFilter;
4014                        var sSpace = oSettings.oLanguage.sSearch==="" ? "" : " ";
4015                        nFilter.innerHTML = oSettings.oLanguage.sSearch+sSpace+'<input type="text" />';
4016                       
4017                        var jqFilter = $("input", nFilter);
4018                        jqFilter.val( oSettings.oPreviousSearch.sSearch.replace('"','&quot;') );
4019                        jqFilter.bind( 'keyup.DT', function(e) {
4020                                /* Update all other filter input elements for the new display */
4021                                var n = oSettings.aanFeatures.f;
4022                                for ( var i=0, iLen=n.length ; i<iLen ; i++ )
4023                                {
4024                                        if ( n[i] != this.parentNode )
4025                                        {
4026                                                $('input', n[i]).val( this.value );
4027                                        }
4028                                }
4029                               
4030                                /* Now do the filter */
4031                                if ( this.value != oSettings.oPreviousSearch.sSearch )
4032                                {
4033                                        _fnFilterComplete( oSettings, {
4034                                                "sSearch": this.value,
4035                                                "bRegex":  oSettings.oPreviousSearch.bRegex,
4036                                                "bSmart":  oSettings.oPreviousSearch.bSmart
4037                                        } );
4038                                }
4039                        } );
4040                       
4041                        jqFilter.bind( 'keypress.DT', function(e) {
4042                                /* Prevent default */
4043                                if ( e.keyCode == 13 )
4044                                {
4045                                        return false;
4046                                }
4047                        } );
4048                       
4049                        return nFilter;
4050                }
4051               
4052                /*
4053                 * Function: _fnFilterComplete
4054                 * Purpose:  Filter the table using both the global filter and column based filtering
4055                 * Returns:  -
4056                 * Inputs:   object:oSettings - dataTables settings object
4057                 *           object:oSearch: search information
4058                 *           int:iForce - optional - force a research of the master array (1) or not (undefined or 0)
4059                 */
4060                function _fnFilterComplete ( oSettings, oInput, iForce )
4061                {
4062                        /* Filter on everything */
4063                        _fnFilter( oSettings, oInput.sSearch, iForce, oInput.bRegex, oInput.bSmart );
4064                       
4065                        /* Now do the individual column filter */
4066                        for ( var i=0 ; i<oSettings.aoPreSearchCols.length ; i++ )
4067                        {
4068                                _fnFilterColumn( oSettings, oSettings.aoPreSearchCols[i].sSearch, i,
4069                                        oSettings.aoPreSearchCols[i].bRegex, oSettings.aoPreSearchCols[i].bSmart );
4070                        }
4071                       
4072                        /* Custom filtering */
4073                        if ( _oExt.afnFiltering.length !== 0 )
4074                        {
4075                                _fnFilterCustom( oSettings );
4076                        }
4077                       
4078                        /* Tell the draw function we have been filtering */
4079                        oSettings.bFiltered = true;
4080                       
4081                        /* Redraw the table */
4082                        oSettings._iDisplayStart = 0;
4083                        _fnCalculateEnd( oSettings );
4084                        _fnDraw( oSettings );
4085                       
4086                        /* Rebuild search array 'offline' */
4087                        _fnBuildSearchArray( oSettings, 0 );
4088                }
4089               
4090                /*
4091                 * Function: _fnFilterCustom
4092                 * Purpose:  Apply custom filtering functions
4093                 * Returns:  -
4094                 * Inputs:   object:oSettings - dataTables settings object
4095                 */
4096                function _fnFilterCustom( oSettings )
4097                {
4098                        var afnFilters = _oExt.afnFiltering;
4099                        for ( var i=0, iLen=afnFilters.length ; i<iLen ; i++ )
4100                        {
4101                                var iCorrector = 0;
4102                                for ( var j=0, jLen=oSettings.aiDisplay.length ; j<jLen ; j++ )
4103                                {
4104                                        var iDisIndex = oSettings.aiDisplay[j-iCorrector];
4105                                       
4106                                        /* Check if we should use this row based on the filtering function */
4107                                        if ( !afnFilters[i]( oSettings, oSettings.aoData[iDisIndex]._aData, iDisIndex ) )
4108                                        {
4109                                                oSettings.aiDisplay.splice( j-iCorrector, 1 );
4110                                                iCorrector++;
4111                                        }
4112                                }
4113                        }
4114                }
4115               
4116                /*
4117                 * Function: _fnFilterColumn
4118                 * Purpose:  Filter the table on a per-column basis
4119                 * Returns:  -
4120                 * Inputs:   object:oSettings - dataTables settings object
4121                 *           string:sInput - string to filter on
4122                 *           int:iColumn - column to filter
4123                 *           bool:bRegex - treat search string as a regular expression or not
4124                 *           bool:bSmart - use smart filtering or not
4125                 */
4126                function _fnFilterColumn ( oSettings, sInput, iColumn, bRegex, bSmart )
4127                {
4128                        if ( sInput === "" )
4129                        {
4130                                return;
4131                        }
4132                       
4133                        var iIndexCorrector = 0;
4134                        var rpSearch = _fnFilterCreateSearch( sInput, bRegex, bSmart );
4135                       
4136                        for ( var i=oSettings.aiDisplay.length-1 ; i>=0 ; i-- )
4137                        {
4138                                var sData = _fnDataToSearch( oSettings.aoData[ oSettings.aiDisplay[i] ]._aData[iColumn],
4139                                        oSettings.aoColumns[iColumn].sType );
4140                                if ( ! rpSearch.test( sData ) )
4141                                {
4142                                        oSettings.aiDisplay.splice( i, 1 );
4143                                        iIndexCorrector++;
4144                                }
4145                        }
4146                }
4147               
4148                /*
4149                 * Function: _fnFilter
4150                 * Purpose:  Filter the data table based on user input and draw the table
4151                 * Returns:  -
4152                 * Inputs:   object:oSettings - dataTables settings object
4153                 *           string:sInput - string to filter on
4154                 *           int:iForce - optional - force a research of the master array (1) or not (undefined or 0)
4155                 *           bool:bRegex - treat as a regular expression or not
4156                 *           bool:bSmart - perform smart filtering or not
4157                 */
4158                function _fnFilter( oSettings, sInput, iForce, bRegex, bSmart )
4159                {
4160                        var i;
4161                        var rpSearch = _fnFilterCreateSearch( sInput, bRegex, bSmart );
4162                       
4163                        /* Check if we are forcing or not - optional parameter */
4164                        if ( typeof iForce == 'undefined' || iForce === null )
4165                        {
4166                                iForce = 0;
4167                        }
4168                       
4169                        /* Need to take account of custom filtering functions - always filter */
4170                        if ( _oExt.afnFiltering.length !== 0 )
4171                        {
4172                                iForce = 1;
4173                        }
4174                       
4175                        /*
4176                         * If the input is blank - we want the full data set
4177                         */
4178                        if ( sInput.length <= 0 )
4179                        {
4180                                oSettings.aiDisplay.splice( 0, oSettings.aiDisplay.length);
4181                                oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
4182                        }
4183                        else
4184                        {
4185                                /*
4186                                 * We are starting a new search or the new search string is smaller
4187                                 * then the old one (i.e. delete). Search from the master array
4188                                 */
4189                                if ( oSettings.aiDisplay.length == oSettings.aiDisplayMaster.length ||
4190                                           oSettings.oPreviousSearch.sSearch.length > sInput.length || iForce == 1 ||
4191                                           sInput.indexOf(oSettings.oPreviousSearch.sSearch) !== 0 )
4192                                {
4193                                        /* Nuke the old display array - we are going to rebuild it */
4194                                        oSettings.aiDisplay.splice( 0, oSettings.aiDisplay.length);
4195                                       
4196                                        /* Force a rebuild of the search array */
4197                                        _fnBuildSearchArray( oSettings, 1 );
4198                                       
4199                                        /* Search through all records to populate the search array
4200                                         * The the oSettings.aiDisplayMaster and asDataSearch arrays have 1 to 1
4201                                         * mapping
4202                                         */
4203                                        for ( i=0 ; i<oSettings.aiDisplayMaster.length ; i++ )
4204                                        {
4205                                                if ( rpSearch.test(oSettings.asDataSearch[i]) )
4206                                                {
4207                                                        oSettings.aiDisplay.push( oSettings.aiDisplayMaster[i] );
4208                                                }
4209                                        }
4210                          }
4211                          else
4212                                {
4213                                /* Using old search array - refine it - do it this way for speed
4214                                 * Don't have to search the whole master array again
4215                                         */
4216                                var iIndexCorrector = 0;
4217                               
4218                                /* Search the current results */
4219                                for ( i=0 ; i<oSettings.asDataSearch.length ; i++ )
4220                                        {
4221                                        if ( ! rpSearch.test(oSettings.asDataSearch[i]) )
4222                                                {
4223                                                oSettings.aiDisplay.splice( i-iIndexCorrector, 1 );
4224                                                iIndexCorrector++;
4225                                        }
4226                                }
4227                          }
4228                        }
4229                        oSettings.oPreviousSearch.sSearch = sInput;
4230                        oSettings.oPreviousSearch.bRegex = bRegex;
4231                        oSettings.oPreviousSearch.bSmart = bSmart;
4232                }
4233               
4234                /*
4235                 * Function: _fnBuildSearchArray
4236                 * Purpose:  Create an array which can be quickly search through
4237                 * Returns:  -
4238                 * Inputs:   object:oSettings - dataTables settings object
4239                 *           int:iMaster - use the master data array - optional
4240                 */
4241                function _fnBuildSearchArray ( oSettings, iMaster )
4242                {
4243                        /* Clear out the old data */
4244                        oSettings.asDataSearch.splice( 0, oSettings.asDataSearch.length );
4245                       
4246                        var aArray = (typeof iMaster != 'undefined' && iMaster == 1) ?
4247                                oSettings.aiDisplayMaster : oSettings.aiDisplay;
4248                       
4249                        for ( var i=0, iLen=aArray.length ; i<iLen ; i++ )
4250                        {
4251                                oSettings.asDataSearch[i] = _fnBuildSearchRow( oSettings,
4252                                        oSettings.aoData[ aArray[i] ]._aData );
4253                        }
4254                }
4255               
4256                /*
4257                 * Function: _fnBuildSearchRow
4258                 * Purpose:  Create a searchable string from a single data row
4259                 * Returns:  -
4260                 * Inputs:   object:oSettings - dataTables settings object
4261                 *           array:aData - aoData[]._aData array to use for the data to search
4262                 */
4263                function _fnBuildSearchRow( oSettings, aData )
4264                {
4265                        var sSearch = '';
4266                        var nTmp = document.createElement('div');
4267                       
4268                        for ( var j=0, jLen=oSettings.aoColumns.length ; j<jLen ; j++ )
4269                        {
4270                                if ( oSettings.aoColumns[j].bSearchable )
4271                                {
4272                                        var sData = aData[j];
4273                                        sSearch += _fnDataToSearch( sData, oSettings.aoColumns[j].sType )+'  ';
4274                                }
4275                        }
4276                       
4277                        /* If it looks like there is an HTML entity in the string, attempt to decode it */
4278                        if ( sSearch.indexOf('&') !== -1 )
4279                        {
4280                                nTmp.innerHTML = sSearch;
4281                                sSearch = nTmp.textContent ? nTmp.textContent : nTmp.innerText;
4282                               
4283                                /* IE and Opera appear to put an newline where there is a <br> tag - remove it */
4284                                sSearch = sSearch.replace(/\n/g," ").replace(/\r/g,"");
4285                        }
4286                       
4287                        return sSearch;
4288                }
4289               
4290                /*
4291                 * Function: _fnFilterCreateSearch
4292                 * Purpose:  Build a regular expression object suitable for searching a table
4293                 * Returns:  RegExp: - constructed object
4294                 * Inputs:   string:sSearch - string to search for
4295                 *           bool:bRegex - treat as a regular expression or not
4296                 *           bool:bSmart - perform smart filtering or not
4297                 */
4298                function _fnFilterCreateSearch( sSearch, bRegex, bSmart )
4299                {
4300                        var asSearch, sRegExpString;
4301                       
4302                        if ( bSmart )
4303                        {
4304                                /* Generate the regular expression to use. Something along the lines of:
4305                                 * ^(?=.*?\bone\b)(?=.*?\btwo\b)(?=.*?\bthree\b).*$
4306                                 */
4307                                asSearch = bRegex ? sSearch.split( ' ' ) : _fnEscapeRegex( sSearch ).split( ' ' );
4308                                sRegExpString = '^(?=.*?'+asSearch.join( ')(?=.*?' )+').*$';
4309                                return new RegExp( sRegExpString, "i" );
4310                        }
4311                        else
4312                        {
4313                                sSearch = bRegex ? sSearch : _fnEscapeRegex( sSearch );
4314                                return new RegExp( sSearch, "i" );
4315                        }
4316                }
4317               
4318                /*
4319                 * Function: _fnDataToSearch
4320                 * Purpose:  Convert raw data into something that the user can search on
4321                 * Returns:  string: - search string
4322                 * Inputs:   string:sData - data to be modified
4323                 *           string:sType - data type
4324                 */
4325                function _fnDataToSearch ( sData, sType )
4326                {
4327                        if ( typeof _oExt.ofnSearch[sType] == "function" )
4328                        {
4329                                return _oExt.ofnSearch[sType]( sData );
4330                        }
4331                        else if ( sType == "html" )
4332                        {
4333                                return sData.replace(/\n/g," ").replace( /<.*?>/g, "" );
4334                        }
4335                        else if ( typeof sData == "string" )
4336                        {
4337                                return sData.replace(/\n/g," ");
4338                        }
4339                        return sData;
4340                }
4341               
4342               
4343                /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
4344                 * Section - Feature: Sorting
4345                 */
4346               
4347                /*
4348                 * Function: _fnSort
4349                 * Purpose:  Change the order of the table
4350                 * Returns:  -
4351                 * Inputs:   object:oSettings - dataTables settings object
4352                 *           bool:bApplyClasses - optional - should we apply classes or not
4353                 * Notes:    We always sort the master array and then apply a filter again
4354                 *   if it is needed. This probably isn't optimal - but atm I can't think
4355                 *   of any other way which is (each has disadvantages). we want to sort aiDisplayMaster -
4356                 *   but according to aoData[]._aData
4357                 */
4358                function _fnSort ( oSettings, bApplyClasses )
4359                {
4360                        var
4361                                iDataSort, iDataType,
4362                                i, iLen, j, jLen,
4363                                aaSort = [],
4364                                aiOrig = [],
4365                                oSort = _oExt.oSort,
4366                                aoData = oSettings.aoData,
4367                                aoColumns = oSettings.aoColumns;
4368                       
4369                        /* No sorting required if server-side or no sorting array */
4370                        if ( !oSettings.oFeatures.bServerSide &&
4371                                (oSettings.aaSorting.length !== 0 || oSettings.aaSortingFixed !== null) )
4372                        {
4373                                if ( oSettings.aaSortingFixed !== null )
4374                                {
4375                                        aaSort = oSettings.aaSortingFixed.concat( oSettings.aaSorting );
4376                                }
4377                                else
4378                                {
4379                                        aaSort = oSettings.aaSorting.slice();
4380                                }
4381                               
4382                                /* If there is a sorting data type, and a fuction belonging to it, then we need to
4383                                 * get the data from the developer's function and apply it for this column
4384                                 */
4385                                for ( i=0 ; i<aaSort.length ; i++ )
4386                                {
4387                                        var iColumn = aaSort[i][0];
4388                                        var iVisColumn = _fnColumnIndexToVisible( oSettings, iColumn );
4389                                        var sDataType = oSettings.aoColumns[ iColumn ].sSortDataType;
4390                                        if ( typeof _oExt.afnSortData[sDataType] != 'undefined' )
4391                                        {
4392                                                var aData = _oExt.afnSortData[sDataType]( oSettings, iColumn, iVisColumn );
4393                                                for ( j=0, jLen=aoData.length ; j<jLen ; j++ )
4394                                                {
4395                                                        aoData[j]._aData[iColumn] = aData[j];
4396                                                }
4397                                        }
4398                                }
4399                               
4400                                /* Create a value - key array of the current row positions such that we can use their
4401                                 * current position during the sort, if values match, in order to perform stable sorting
4402                                 */
4403                                for ( i=0, iLen=oSettings.aiDisplayMaster.length ; i<iLen ; i++ )
4404                                {
4405                                        aiOrig[ oSettings.aiDisplayMaster[i] ] = i;
4406                                }
4407                               
4408                                /* Do the sort - here we want multi-column sorting based on a given data source (column)
4409                                 * and sorting function (from oSort) in a certain direction. It's reasonably complex to
4410                                 * follow on it's own, but this is what we want (example two column sorting):
4411                                 *  fnLocalSorting = function(a,b){
4412                                 *      var iTest;
4413                                 *      iTest = oSort['string-asc']('data11', 'data12');
4414                                 *      if (iTest !== 0)
4415                                 *              return iTest;
4416                                 *    iTest = oSort['numeric-desc']('data21', 'data22');
4417                                 *    if (iTest !== 0)
4418                                 *              return iTest;
4419                                 *      return oSort['numeric-asc']( aiOrig[a], aiOrig[b] );
4420                                 *  }
4421                                 * Basically we have a test for each sorting column, if the data in that column is equal,
4422                                 * test the next column. If all columns match, then we use a numeric sort on the row
4423                                 * positions in the original data array to provide a stable sort.
4424                                 */
4425                                var iSortLen = aaSort.length;
4426                                oSettings.aiDisplayMaster.sort( function ( a, b ) {
4427                                        var iTest;
4428                                        for ( i=0 ; i<iSortLen ; i++ )
4429                                        {
4430                                                iDataSort = aoColumns[ aaSort[i][0] ].iDataSort;
4431                                                iDataType = aoColumns[ iDataSort ].sType;
4432                                                iTest = oSort[ iDataType+"-"+aaSort[i][1] ](
4433                                                        aoData[a]._aData[iDataSort],
4434                                                        aoData[b]._aData[iDataSort]
4435                                                );
4436                                               
4437                                                if ( iTest !== 0 )
4438                                                {
4439                                                        return iTest;
4440                                                }
4441                                        }
4442                                       
4443                                        return oSort['numeric-asc']( aiOrig[a], aiOrig[b] );
4444                                } );
4445                        }
4446                       
4447                        /* Alter the sorting classes to take account of the changes */
4448                        if ( typeof bApplyClasses == 'undefined' || bApplyClasses )
4449                        {
4450                                _fnSortingClasses( oSettings );
4451                        }
4452                       
4453                        /* Tell the draw function that we have sorted the data */
4454                        oSettings.bSorted = true;
4455                       
4456                        /* Copy the master data into the draw array and re-draw */
4457                        if ( oSettings.oFeatures.bFilter )
4458                        {
4459                                /* _fnFilter() will redraw the table for us */
4460                                _fnFilterComplete( oSettings, oSettings.oPreviousSearch, 1 );
4461                        }
4462                        else
4463                        {
4464                                oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
4465                                oSettings._iDisplayStart = 0; /* reset display back to page 0 */
4466                                _fnCalculateEnd( oSettings );
4467                                _fnDraw( oSettings );
4468                        }
4469                }
4470               
4471                /*
4472                 * Function: _fnSortAttachListener
4473                 * Purpose:  Attach a sort handler (click) to a node
4474                 * Returns:  -
4475                 * Inputs:   object:oSettings - dataTables settings object
4476                 *           node:nNode - node to attach the handler to
4477                 *           int:iDataIndex - column sorting index
4478                 *           function:fnCallback - callback function - optional
4479                 */
4480                function _fnSortAttachListener ( oSettings, nNode, iDataIndex, fnCallback )
4481                {
4482                        $(nNode).bind( 'click.DT', function (e) {
4483                                /* If the column is not sortable - don't to anything */
4484                                if ( oSettings.aoColumns[iDataIndex].bSortable === false )
4485                                {
4486                                        return;
4487                                }
4488                               
4489                                /*
4490                                 * This is a little bit odd I admit... I declare a temporary function inside the scope of
4491                                 * _fnDrawHead and the click handler in order that the code presented here can be used
4492                                 * twice - once for when bProcessing is enabled, and another time for when it is
4493                                 * disabled, as we need to perform slightly different actions.
4494                                 *   Basically the issue here is that the Javascript engine in modern browsers don't
4495                                 * appear to allow the rendering engine to update the display while it is still excuting
4496                                 * it's thread (well - it does but only after long intervals). This means that the
4497                                 * 'processing' display doesn't appear for a table sort. To break the js thread up a bit
4498                                 * I force an execution break by using setTimeout - but this breaks the expected
4499                                 * thread continuation for the end-developer's point of view (their code would execute
4500                                 * too early), so we on;y do it when we absolutely have to.
4501                                 */
4502                                var fnInnerSorting = function () {
4503                                        var iColumn, iNextSort;
4504                                       
4505                                        /* If the shift key is pressed then we are multipe column sorting */
4506                                        if ( e.shiftKey )
4507                                        {
4508                                                /* Are we already doing some kind of sort on this column? */
4509                                                var bFound = false;
4510                                                for ( var i=0 ; i<oSettings.aaSorting.length ; i++ )
4511                                                {
4512                                                        if ( oSettings.aaSorting[i][0] == iDataIndex )
4513                                                        {
4514                                                                bFound = true;
4515                                                                iColumn = oSettings.aaSorting[i][0];
4516                                                                iNextSort = oSettings.aaSorting[i][2]+1;
4517                                                               
4518                                                                if ( typeof oSettings.aoColumns[iColumn].asSorting[iNextSort] == 'undefined' )
4519                                                                {
4520                                                                        /* Reached the end of the sorting options, remove from multi-col sort */
4521                                                                        oSettings.aaSorting.splice( i, 1 );
4522                                                                }
4523                                                                else
4524                                                                {
4525                                                                        /* Move onto next sorting direction */
4526                                                                        oSettings.aaSorting[i][1] = oSettings.aoColumns[iColumn].asSorting[iNextSort];
4527                                                                        oSettings.aaSorting[i][2] = iNextSort;
4528                                                                }
4529                                                                break;
4530                                                        }
4531                                                }
4532                                               
4533                                                /* No sort yet - add it in */
4534                                                if ( bFound === false )
4535                                                {
4536                                                        oSettings.aaSorting.push( [ iDataIndex,
4537                                                                oSettings.aoColumns[iDataIndex].asSorting[0], 0 ] );
4538                                                }
4539                                        }
4540                                        else
4541                                        {
4542                                                /* If no shift key then single column sort */
4543                                                if ( oSettings.aaSorting.length == 1 && oSettings.aaSorting[0][0] == iDataIndex )
4544                                                {
4545                                                        iColumn = oSettings.aaSorting[0][0];
4546                                                        iNextSort = oSettings.aaSorting[0][2]+1;
4547                                                        if ( typeof oSettings.aoColumns[iColumn].asSorting[iNextSort] == 'undefined' )
4548                                                        {
4549                                                                iNextSort = 0;
4550                                                        }
4551                                                        oSettings.aaSorting[0][1] = oSettings.aoColumns[iColumn].asSorting[iNextSort];
4552                                                        oSettings.aaSorting[0][2] = iNextSort;
4553                                                }
4554                                                else
4555                                                {
4556                                                        oSettings.aaSorting.splice( 0, oSettings.aaSorting.length );
4557                                                        oSettings.aaSorting.push( [ iDataIndex,
4558                                                                oSettings.aoColumns[iDataIndex].asSorting[0], 0 ] );
4559                                                }
4560                                        }
4561                                       
4562                                        /* Run the sort */
4563                                        _fnSort( oSettings );
4564                                }; /* /fnInnerSorting */
4565                               
4566                                if ( !oSettings.oFeatures.bProcessing )
4567                                {
4568                                        fnInnerSorting();
4569                                }
4570                                else
4571                                {
4572                                        _fnProcessingDisplay( oSettings, true );
4573                                        setTimeout( function() {
4574                                                fnInnerSorting();
4575                                                if ( !oSettings.oFeatures.bServerSide )
4576                                                {
4577                                                        _fnProcessingDisplay( oSettings, false );
4578                                                }
4579                                        }, 0 );
4580                                }
4581                               
4582                                /* Call the user specified callback function - used for async user interaction */
4583                                if ( typeof fnCallback == 'function' )
4584                                {
4585                                        fnCallback( oSettings );
4586                                }
4587                        } );
4588                }
4589               
4590                /*
4591                 * Function: _fnSortingClasses
4592                 * Purpose:  Set the sortting classes on the header
4593                 * Returns:  -
4594                 * Inputs:   object:oSettings - dataTables settings object
4595                 * Notes:    It is safe to call this function when bSort and bSortClasses are false
4596                 */
4597                function _fnSortingClasses( oSettings )
4598                {
4599                        var i, iLen, j, jLen, iFound;
4600                        var aaSort, sClass;
4601                        var iColumns = oSettings.aoColumns.length;
4602                        var oClasses = oSettings.oClasses;
4603                       
4604                        for ( i=0 ; i<iColumns ; i++ )
4605                        {
4606                                if ( oSettings.aoColumns[i].bSortable )
4607                                {
4608                                        $(oSettings.aoColumns[i].nTh).removeClass( oClasses.sSortAsc +" "+ oClasses.sSortDesc +
4609                                                " "+ oSettings.aoColumns[i].sSortingClass );
4610                                }
4611                        }
4612                       
4613                        if ( oSettings.aaSortingFixed !== null )
4614                        {
4615                                aaSort = oSettings.aaSortingFixed.concat( oSettings.aaSorting );
4616                        }
4617                        else
4618                        {
4619                                aaSort = oSettings.aaSorting.slice();
4620                        }
4621                       
4622                        /* Apply the required classes to the header */
4623                        for ( i=0 ; i<oSettings.aoColumns.length ; i++ )
4624                        {
4625                                if ( oSettings.aoColumns[i].bSortable )
4626                                {
4627                                        sClass = oSettings.aoColumns[i].sSortingClass;
4628                                        iFound = -1;
4629                                        for ( j=0 ; j<aaSort.length ; j++ )
4630                                        {
4631                                                if ( aaSort[j][0] == i )
4632                                                {
4633                                                        sClass = ( aaSort[j][1] == "asc" ) ?
4634                                                                oClasses.sSortAsc : oClasses.sSortDesc;
4635                                                        iFound = j;
4636                                                        break;
4637                                                }
4638                                        }
4639                                        $(oSettings.aoColumns[i].nTh).addClass( sClass );
4640                                       
4641                                        if ( oSettings.bJUI )
4642                                        {
4643                                                /* jQuery UI uses extra markup */
4644                                                var jqSpan = $("span", oSettings.aoColumns[i].nTh);
4645                                                jqSpan.removeClass(oClasses.sSortJUIAsc +" "+ oClasses.sSortJUIDesc +" "+
4646                                                        oClasses.sSortJUI +" "+ oClasses.sSortJUIAscAllowed +" "+ oClasses.sSortJUIDescAllowed );
4647                                               
4648                                                var sSpanClass;
4649                                                if ( iFound == -1 )
4650                                                {
4651                                                        sSpanClass = oSettings.aoColumns[i].sSortingClassJUI;
4652                                                }
4653                                                else if ( aaSort[iFound][1] == "asc" )
4654                                                {
4655                                                        sSpanClass = oClasses.sSortJUIAsc;
4656                                                }
4657                                                else
4658                                                {
4659                                                        sSpanClass = oClasses.sSortJUIDesc;
4660                                                }
4661                                               
4662                                                jqSpan.addClass( sSpanClass );
4663                                        }
4664                                }
4665                                else
4666                                {
4667                                        /* No sorting on this column, so add the base class. This will have been assigned by
4668                                         * _fnAddColumn
4669                                         */
4670                                        $(oSettings.aoColumns[i].nTh).addClass( oSettings.aoColumns[i].sSortingClass );
4671                                }
4672                        }
4673                       
4674                        /*
4675                         * Apply the required classes to the table body
4676                         * Note that this is given as a feature switch since it can significantly slow down a sort
4677                         * on large data sets (adding and removing of classes is always slow at the best of times..)
4678                         * Further to this, note that this code is admitadly fairly ugly. It could be made a lot
4679                         * simpiler using jQuery selectors and add/removeClass, but that is significantly slower
4680                         * (on the order of 5 times slower) - hence the direct DOM manipulation here.
4681                         */
4682                        sClass = oClasses.sSortColumn;
4683                       
4684                        if ( oSettings.oFeatures.bSort && oSettings.oFeatures.bSortClasses )
4685                        {
4686                                var nTds = _fnGetTdNodes( oSettings );
4687                               
4688                                /* Remove the old classes */
4689                                if ( nTds.length >= iColumns )
4690                                {
4691                                        for ( i=0 ; i<iColumns ; i++ )
4692                                        {
4693                                                if ( nTds[i].className.indexOf(sClass+"1") != -1 )
4694                                                {
4695                                                        for ( j=0, jLen=(nTds.length/iColumns) ; j<jLen ; j++ )
4696                                                        {
4697                                                                nTds[(iColumns*j)+i].className =
4698                                                                        $.trim( nTds[(iColumns*j)+i].className.replace( sClass+"1", "" ) );
4699                                                        }
4700                                                }
4701                                                else if ( nTds[i].className.indexOf(sClass+"2") != -1 )
4702                                                {
4703                                                        for ( j=0, jLen=(nTds.length/iColumns) ; j<jLen ; j++ )
4704                                                        {
4705                                                                nTds[(iColumns*j)+i].className =
4706                                                                        $.trim( nTds[(iColumns*j)+i].className.replace( sClass+"2", "" ) );
4707                                                        }
4708                                                }
4709                                                else if ( nTds[i].className.indexOf(sClass+"3") != -1 )
4710                                                {
4711                                                        for ( j=0, jLen=(nTds.length/iColumns) ; j<jLen ; j++ )
4712                                                        {
4713                                                                nTds[(iColumns*j)+i].className =
4714                                                                        $.trim( nTds[(iColumns*j)+i].className.replace( " "+sClass+"3", "" ) );
4715                                                        }
4716                                                }
4717                                        }
4718                                }
4719                               
4720                                /* Add the new classes to the table */
4721                                var iClass = 1, iTargetCol;
4722                                for ( i=0 ; i<aaSort.length ; i++ )
4723                                {
4724                                        iTargetCol = parseInt( aaSort[i][0], 10 );
4725                                        for ( j=0, jLen=(nTds.length/iColumns) ; j<jLen ; j++ )
4726                                        {
4727                                                nTds[(iColumns*j)+iTargetCol].className += " "+sClass+iClass;
4728                                        }
4729                                       
4730                                        if ( iClass < 3 )
4731                                        {
4732                                                iClass++;
4733                                        }
4734                                }
4735                        }
4736                }
4737               
4738               
4739                /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
4740                 * Section - Feature: Pagination. Note that most of the paging logic is done in
4741                 * _oExt.oPagination
4742                 */
4743               
4744                /*
4745                 * Function: _fnFeatureHtmlPaginate
4746                 * Purpose:  Generate the node required for default pagination
4747                 * Returns:  node
4748                 * Inputs:   object:oSettings - dataTables settings object
4749                 */
4750                function _fnFeatureHtmlPaginate ( oSettings )
4751                {
4752                        if ( oSettings.oScroll.bInfinite )
4753                        {
4754                                return null;
4755                        }
4756                       
4757                        var nPaginate = document.createElement( 'div' );
4758                        nPaginate.className = oSettings.oClasses.sPaging+oSettings.sPaginationType;
4759                       
4760                        _oExt.oPagination[ oSettings.sPaginationType ].fnInit( oSettings, nPaginate,
4761                                function( oSettings ) {
4762                                        _fnCalculateEnd( oSettings );
4763                                        _fnDraw( oSettings );
4764                                }
4765                        );
4766                       
4767                        /* Add a draw callback for the pagination on first instance, to update the paging display */
4768                        if ( typeof oSettings.aanFeatures.p == "undefined" )
4769                        {
4770                                oSettings.aoDrawCallback.push( {
4771                                        "fn": function( oSettings ) {
4772                                                _oExt.oPagination[ oSettings.sPaginationType ].fnUpdate( oSettings, function( oSettings ) {
4773                                                        _fnCalculateEnd( oSettings );
4774                                                        _fnDraw( oSettings );
4775                                                } );
4776                                        },
4777                                        "sName": "pagination"
4778                                } );
4779                        }
4780                        return nPaginate;
4781                }
4782               
4783                /*
4784                 * Function: _fnPageChange
4785                 * Purpose:  Alter the display settings to change the page
4786                 * Returns:  bool:true - page has changed, false - no change (no effect) eg 'first' on page 1
4787                 * Inputs:   object:oSettings - dataTables settings object
4788                 *           string:sAction - paging action to take: "first", "previous", "next" or "last"
4789                 */
4790                function _fnPageChange ( oSettings, sAction )
4791                {
4792                        var iOldStart = oSettings._iDisplayStart;
4793                       
4794                        if ( sAction == "first" )
4795                        {
4796                                oSettings._iDisplayStart = 0;
4797                        }
4798                        else if ( sAction == "previous" )
4799                        {
4800                                oSettings._iDisplayStart = oSettings._iDisplayLength>=0 ?
4801                                        oSettings._iDisplayStart - oSettings._iDisplayLength :
4802                                        0;
4803                               
4804                                /* Correct for underrun */
4805                                if ( oSettings._iDisplayStart < 0 )
4806                                {
4807                                  oSettings._iDisplayStart = 0;
4808                                }
4809                        }
4810                        else if ( sAction == "next" )
4811                        {
4812                                if ( oSettings._iDisplayLength >= 0 )
4813                                {
4814                                        /* Make sure we are not over running the display array */
4815                                        if ( oSettings._iDisplayStart + oSettings._iDisplayLength < oSettings.fnRecordsDisplay() )
4816                                        {
4817                                                oSettings._iDisplayStart += oSettings._iDisplayLength;
4818                                        }
4819                                }
4820                                else
4821                                {
4822                                        oSettings._iDisplayStart = 0;
4823                                }
4824                        }
4825                        else if ( sAction == "last" )
4826                        {
4827                                if ( oSettings._iDisplayLength >= 0 )
4828                                {
4829                                        var iPages = parseInt( (oSettings.fnRecordsDisplay()-1) / oSettings._iDisplayLength, 10 ) + 1;
4830                                        oSettings._iDisplayStart = (iPages-1) * oSettings._iDisplayLength;
4831                                }
4832                                else
4833                                {
4834                                        oSettings._iDisplayStart = 0;
4835                                }
4836                        }
4837                        else
4838                        {
4839                                _fnLog( oSettings, 0, "Unknown paging action: "+sAction );
4840                        }
4841                       
4842                        return iOldStart != oSettings._iDisplayStart;
4843                }
4844               
4845               
4846                /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
4847                 * Section - Feature: HTML info
4848                 */
4849               
4850                /*
4851                 * Function: _fnFeatureHtmlInfo
4852                 * Purpose:  Generate the node required for the info display
4853                 * Returns:  node
4854                 * Inputs:   object:oSettings - dataTables settings object
4855                 */
4856                function _fnFeatureHtmlInfo ( oSettings )
4857                {
4858                        var nInfo = document.createElement( 'div' );
4859                        nInfo.className = oSettings.oClasses.sInfo;
4860                       
4861                        /* Actions that are to be taken once only for this feature */
4862                        if ( typeof oSettings.aanFeatures.i == "undefined" )
4863                        {
4864                                /* Add draw callback */
4865                                oSettings.aoDrawCallback.push( {
4866                                        "fn": _fnUpdateInfo,
4867                                        "sName": "information"
4868                                } );
4869                               
4870                                /* Add id */
4871                                if ( oSettings.sTableId !== '' )
4872                                {
4873                                        nInfo.setAttribute( 'id', oSettings.sTableId+'_info' );
4874                                }
4875                        }
4876                       
4877                        return nInfo;
4878                }
4879               
4880                /*
4881                 * Function: _fnUpdateInfo
4882                 * Purpose:  Update the information elements in the display
4883                 * Returns:  -
4884                 * Inputs:   object:oSettings - dataTables settings object
4885                 */
4886                function _fnUpdateInfo ( oSettings )
4887                {
4888                        /* Show information about the table */
4889                        if ( !oSettings.oFeatures.bInfo || oSettings.aanFeatures.i.length === 0 )
4890                        {
4891                                return;
4892                        }
4893                       
4894                        var
4895                                iStart = oSettings._iDisplayStart+1, iEnd = oSettings.fnDisplayEnd(),
4896                                iMax = oSettings.fnRecordsTotal(), iTotal = oSettings.fnRecordsDisplay(),
4897                                sStart = oSettings.fnFormatNumber( iStart ), sEnd = oSettings.fnFormatNumber( iEnd ),
4898                                sMax = oSettings.fnFormatNumber( iMax ), sTotal = oSettings.fnFormatNumber( iTotal ),
4899                                sOut;
4900                       
4901                        /* When infinite scrolling, we are always starting at 1. _iDisplayStart is used only
4902                         * internally
4903                         */
4904                        if ( oSettings.oScroll.bInfinite )
4905                        {
4906                                sStart = oSettings.fnFormatNumber( 1 );
4907                        }
4908                       
4909                        if ( oSettings.fnRecordsDisplay() === 0 &&
4910                                   oSettings.fnRecordsDisplay() == oSettings.fnRecordsTotal() )
4911                        {
4912                                /* Empty record set */
4913                                sOut = oSettings.oLanguage.sInfoEmpty+ oSettings.oLanguage.sInfoPostFix;
4914                        }
4915                        else if ( oSettings.fnRecordsDisplay() === 0 )
4916                        {
4917                                /* Rmpty record set after filtering */
4918                                sOut = oSettings.oLanguage.sInfoEmpty +' '+
4919                                        oSettings.oLanguage.sInfoFiltered.replace('_MAX_', sMax)+
4920                                                oSettings.oLanguage.sInfoPostFix;
4921                        }
4922                        else if ( oSettings.fnRecordsDisplay() == oSettings.fnRecordsTotal() )
4923                        {
4924                                /* Normal record set */
4925                                sOut = oSettings.oLanguage.sInfo.
4926                                                replace('_START_', sStart).
4927                                                replace('_END_',   sEnd).
4928                                                replace('_TOTAL_', sTotal)+
4929                                        oSettings.oLanguage.sInfoPostFix;
4930                        }
4931                        else
4932                        {
4933                                /* Record set after filtering */
4934                                sOut = oSettings.oLanguage.sInfo.
4935                                                replace('_START_', sStart).
4936                                                replace('_END_',   sEnd).
4937                                                replace('_TOTAL_', sTotal) +' '+
4938                                        oSettings.oLanguage.sInfoFiltered.replace('_MAX_',
4939                                                oSettings.fnFormatNumber(oSettings.fnRecordsTotal()))+
4940                                        oSettings.oLanguage.sInfoPostFix;
4941                        }
4942                       
4943                        if ( oSettings.oLanguage.fnInfoCallback !== null )
4944                        {
4945                                sOut = oSettings.oLanguage.fnInfoCallback( oSettings, iStart, iEnd, iMax, iTotal, sOut );
4946                        }
4947                       
4948                        var n = oSettings.aanFeatures.i;
4949                        for ( var i=0, iLen=n.length ; i<iLen ; i++ )
4950                        {
4951                                $(n[i]).html( sOut );
4952                        }
4953                }
4954               
4955               
4956                /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
4957                 * Section - Feature: Length change
4958                 */
4959               
4960                /*
4961                 * Function: _fnFeatureHtmlLength
4962                 * Purpose:  Generate the node required for user display length changing
4963                 * Returns:  node
4964                 * Inputs:   object:oSettings - dataTables settings object
4965                 */
4966                function _fnFeatureHtmlLength ( oSettings )
4967                {
4968                        if ( oSettings.oScroll.bInfinite )
4969                        {
4970                                return null;
4971                        }
4972                       
4973                        /* This can be overruled by not using the _MENU_ var/macro in the language variable */
4974                        var sName = (oSettings.sTableId === "") ? "" : 'name="'+oSettings.sTableId+'_length"';
4975                        var sStdMenu = '<select size="1" '+sName+'>';
4976                        var i, iLen;
4977                       
4978                        if ( oSettings.aLengthMenu.length == 2 && typeof oSettings.aLengthMenu[0] == 'object' &&
4979                                        typeof oSettings.aLengthMenu[1] == 'object' )
4980                        {
4981                                for ( i=0, iLen=oSettings.aLengthMenu[0].length ; i<iLen ; i++ )
4982                                {
4983                                        sStdMenu += '<option value="'+oSettings.aLengthMenu[0][i]+'">'+
4984                                                oSettings.aLengthMenu[1][i]+'</option>';
4985                                }
4986                        }
4987                        else
4988                        {
4989                                for ( i=0, iLen=oSettings.aLengthMenu.length ; i<iLen ; i++ )
4990                                {
4991                                        sStdMenu += '<option value="'+oSettings.aLengthMenu[i]+'">'+
4992                                                oSettings.aLengthMenu[i]+'</option>';
4993                                }
4994                        }
4995                        sStdMenu += '</select>';
4996                       
4997                        var nLength = document.createElement( 'div' );
4998                        if ( oSettings.sTableId !== '' && typeof oSettings.aanFeatures.l == "undefined" )
4999                        {
5000                                nLength.setAttribute( 'id', oSettings.sTableId+'_length' );
5001                        }
5002                        nLength.className = oSettings.oClasses.sLength;
5003                        nLength.innerHTML = oSettings.oLanguage.sLengthMenu.replace( '_MENU_', sStdMenu );
5004                       
5005                        /*
5006                         * Set the length to the current display length - thanks to Andrea Pavlovic for this fix,
5007                         * and Stefan Skopnik for fixing the fix!
5008                         */
5009                        $('select option[value="'+oSettings._iDisplayLength+'"]',nLength).attr("selected",true);
5010                       
5011                        $('select', nLength).bind( 'change.DT', function(e) {
5012                                var iVal = $(this).val();
5013                               
5014                                /* Update all other length options for the new display */
5015                                var n = oSettings.aanFeatures.l;
5016                                for ( i=0, iLen=n.length ; i<iLen ; i++ )
5017                                {
5018                                        if ( n[i] != this.parentNode )
5019                                        {
5020                                                $('select', n[i]).val( iVal );
5021                                        }
5022                                }
5023                               
5024                                /* Redraw the table */
5025                                oSettings._iDisplayLength = parseInt(iVal, 10);
5026                                _fnCalculateEnd( oSettings );
5027                               
5028                                /* If we have space to show extra rows (backing up from the end point - then do so */
5029                                if ( oSettings.fnDisplayEnd() == oSettings.fnRecordsDisplay() )
5030                                {
5031                                        oSettings._iDisplayStart = oSettings.fnDisplayEnd() - oSettings._iDisplayLength;
5032                                        if ( oSettings._iDisplayStart < 0 )
5033                                        {
5034                                                oSettings._iDisplayStart = 0;
5035                                        }
5036                                }
5037                               
5038                                if ( oSettings._iDisplayLength == -1 )
5039                                {
5040                                        oSettings._iDisplayStart = 0;
5041                                }
5042                               
5043                                _fnDraw( oSettings );
5044                        } );
5045                       
5046                        return nLength;
5047                }
5048               
5049               
5050                /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
5051                 * Section - Feature: Processing incidator
5052                 */
5053               
5054                /*
5055                 * Function: _fnFeatureHtmlProcessing
5056                 * Purpose:  Generate the node required for the processing node
5057                 * Returns:  node
5058                 * Inputs:   object:oSettings - dataTables settings object
5059                 */
5060                function _fnFeatureHtmlProcessing ( oSettings )
5061                {
5062                        var nProcessing = document.createElement( 'div' );
5063                       
5064                        if ( oSettings.sTableId !== '' && typeof oSettings.aanFeatures.r == "undefined" )
5065                        {
5066                                nProcessing.setAttribute( 'id', oSettings.sTableId+'_processing' );
5067                        }
5068                        nProcessing.innerHTML = oSettings.oLanguage.sProcessing;
5069                        nProcessing.className = oSettings.oClasses.sProcessing;
5070                        oSettings.nTable.parentNode.insertBefore( nProcessing, oSettings.nTable );
5071                       
5072                        return nProcessing;
5073                }
5074               
5075                /*
5076                 * Function: _fnProcessingDisplay
5077                 * Purpose:  Display or hide the processing indicator
5078                 * Returns:  -
5079                 * Inputs:   object:oSettings - dataTables settings object
5080                 *           bool:
5081                 *   true - show the processing indicator
5082                 *   false - don't show
5083                 */
5084                function _fnProcessingDisplay ( oSettings, bShow )
5085                {
5086                        if ( oSettings.oFeatures.bProcessing )
5087                        {
5088                                var an = oSettings.aanFeatures.r;
5089                                for ( var i=0, iLen=an.length ; i<iLen ; i++ )
5090                                {
5091                                        an[i].style.visibility = bShow ? "visible" : "hidden";
5092                                }
5093                        }
5094                }
5095               
5096               
5097                /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
5098                 * Section - Support functions
5099                 */
5100               
5101                /*
5102                 * Function: _fnVisibleToColumnIndex
5103                 * Purpose:  Covert the index of a visible column to the index in the data array (take account
5104                 *   of hidden columns)
5105                 * Returns:  int:i - the data index
5106                 * Inputs:   object:oSettings - dataTables settings object
5107                 */
5108                function _fnVisibleToColumnIndex( oSettings, iMatch )
5109                {
5110                        var iColumn = -1;
5111                       
5112                        for ( var i=0 ; i<oSettings.aoColumns.length ; i++ )
5113                        {
5114                                if ( oSettings.aoColumns[i].bVisible === true )
5115                                {
5116                                        iColumn++;
5117                                }
5118                               
5119                                if ( iColumn == iMatch )
5120                                {
5121                                        return i;
5122                                }
5123                        }
5124                       
5125                        return null;
5126                }
5127               
5128                /*
5129                 * Function: _fnColumnIndexToVisible
5130                 * Purpose:  Covert the index of an index in the data array and convert it to the visible
5131                 *   column index (take account of hidden columns)
5132                 * Returns:  int:i - the data index
5133                 * Inputs:   object:oSettings - dataTables settings object
5134                 */
5135                function _fnColumnIndexToVisible( oSettings, iMatch )
5136                {
5137                        var iVisible = -1;
5138                        for ( var i=0 ; i<oSettings.aoColumns.length ; i++ )
5139                        {
5140                                if ( oSettings.aoColumns[i].bVisible === true )
5141                                {
5142                                        iVisible++;
5143                                }
5144                               
5145                                if ( i == iMatch )
5146                                {
5147                                        return oSettings.aoColumns[i].bVisible === true ? iVisible : null;
5148                                }
5149                        }
5150                       
5151                        return null;
5152                }
5153               
5154               
5155                /*
5156                 * Function: _fnNodeToDataIndex
5157                 * Purpose:  Take a TR element and convert it to an index in aoData
5158                 * Returns:  int:i - index if found, null if not
5159                 * Inputs:   object:s - dataTables settings object
5160                 *           node:n - the TR element to find
5161                 */
5162                function _fnNodeToDataIndex( s, n )
5163                {
5164                        var i, iLen;
5165                       
5166                        /* Optimisation - see if the nodes which are currently visible match, since that is
5167                         * the most likely node to be asked for (a selector or event for example)
5168                         */
5169                        for ( i=s._iDisplayStart, iLen=s._iDisplayEnd ; i<iLen ; i++ )
5170                        {
5171                                if ( s.aoData[ s.aiDisplay[i] ].nTr == n )
5172                                {
5173                                        return s.aiDisplay[i];
5174                                }
5175                        }
5176                       
5177                        /* Otherwise we are in for a slog through the whole data cache */
5178                        for ( i=0, iLen=s.aoData.length ; i<iLen ; i++ )
5179                        {
5180                                if ( s.aoData[i].nTr == n )
5181                                {
5182                                        return i;
5183                                }
5184                        }
5185                        return null;
5186                }
5187               
5188                /*
5189                 * Function: _fnVisbleColumns
5190                 * Purpose:  Get the number of visible columns
5191                 * Returns:  int:i - the number of visible columns
5192                 * Inputs:   object:oS - dataTables settings object
5193                 */
5194                function _fnVisbleColumns( oS )
5195                {
5196                        var iVis = 0;
5197                        for ( var i=0 ; i<oS.aoColumns.length ; i++ )
5198                        {
5199                                if ( oS.aoColumns[i].bVisible === true )
5200                                {
5201                                        iVis++;
5202                                }
5203                        }
5204                        return iVis;
5205                }
5206               
5207                /*
5208                 * Function: _fnCalculateEnd
5209                 * Purpose:  Rcalculate the end point based on the start point
5210                 * Returns:  -
5211                 * Inputs:   object:oSettings - dataTables settings object
5212                 */
5213                function _fnCalculateEnd( oSettings )
5214                {
5215                        if ( oSettings.oFeatures.bPaginate === false )
5216                        {
5217                                oSettings._iDisplayEnd = oSettings.aiDisplay.length;
5218                        }
5219                        else
5220                        {
5221                                /* Set the end point of the display - based on how many elements there are
5222                                 * still to display
5223                                 */
5224                                if ( oSettings._iDisplayStart + oSettings._iDisplayLength > oSettings.aiDisplay.length ||
5225                                           oSettings._iDisplayLength == -1 )
5226                                {
5227                                        oSettings._iDisplayEnd = oSettings.aiDisplay.length;
5228                                }
5229                                else
5230                                {
5231                                        oSettings._iDisplayEnd = oSettings._iDisplayStart + oSettings._iDisplayLength;
5232                                }
5233                        }
5234                }
5235               
5236                /*
5237                 * Function: _fnConvertToWidth
5238                 * Purpose:  Convert a CSS unit width to pixels (e.g. 2em)
5239                 * Returns:  int:iWidth - width in pixels
5240                 * Inputs:   string:sWidth - width to be converted
5241                 *           node:nParent - parent to get the with for (required for
5242                 *             relative widths) - optional
5243                 */
5244                function _fnConvertToWidth ( sWidth, nParent )
5245                {
5246                        if ( !sWidth || sWidth === null || sWidth === '' )
5247                        {
5248                                return 0;
5249                        }
5250                       
5251                        if ( typeof nParent == "undefined" )
5252                        {
5253                                nParent = document.getElementsByTagName('body')[0];
5254                        }
5255                       
5256                        var iWidth;
5257                        var nTmp = document.createElement( "div" );
5258                        nTmp.style.width = sWidth;
5259                       
5260                        nParent.appendChild( nTmp );
5261                        iWidth = nTmp.offsetWidth;
5262                        nParent.removeChild( nTmp );
5263                       
5264                        return ( iWidth );
5265                }
5266               
5267                /*
5268                 * Function: _fnCalculateColumnWidths
5269                 * Purpose:  Calculate the width of columns for the table
5270                 * Returns:  -
5271                 * Inputs:   object:oSettings - dataTables settings object
5272                 */
5273                function _fnCalculateColumnWidths ( oSettings )
5274                {
5275                        var iTableWidth = oSettings.nTable.offsetWidth;
5276                        var iUserInputs = 0;
5277                        var iTmpWidth;
5278                        var iVisibleColumns = 0;
5279                        var iColums = oSettings.aoColumns.length;
5280                        var i;
5281                        var oHeaders = $('th', oSettings.nTHead);
5282                       
5283                        /* Convert any user input sizes into pixel sizes */
5284                        for ( i=0 ; i<iColums ; i++ )
5285                        {
5286                                if ( oSettings.aoColumns[i].bVisible )
5287                                {
5288                                        iVisibleColumns++;
5289                                       
5290                                        if ( oSettings.aoColumns[i].sWidth !== null )
5291                                        {
5292                                                iTmpWidth = _fnConvertToWidth( oSettings.aoColumns[i].sWidthOrig,
5293                                                        oSettings.nTable.parentNode );
5294                                                if ( iTmpWidth !== null )
5295                                                {
5296                                                        oSettings.aoColumns[i].sWidth = _fnStringToCss( iTmpWidth );
5297                                                }
5298                                                       
5299                                                iUserInputs++;
5300                                        }
5301                                }
5302                        }
5303                       
5304                        /* If the number of columns in the DOM equals the number that we have to process in
5305                         * DataTables, then we can use the offsets that are created by the web-browser. No custom
5306                         * sizes can be set in order for this to happen, nor scrolling used
5307                         */
5308                        if ( iColums == oHeaders.length && iUserInputs === 0 && iVisibleColumns == iColums &&
5309                                oSettings.oScroll.sX === "" && oSettings.oScroll.sY === "" )
5310                        {
5311                                for ( i=0 ; i<oSettings.aoColumns.length ; i++ )
5312                                {
5313                                        iTmpWidth = $(oHeaders[i]).width();
5314                                        if ( iTmpWidth !== null )
5315                                        {
5316                                                oSettings.aoColumns[i].sWidth = _fnStringToCss( iTmpWidth );
5317                                        }
5318                                }
5319                        }
5320                        else
5321                        {
5322                                /* Otherwise we are going to have to do some calculations to get the width of each column.
5323                                 * Construct a 1 row table with the widest node in the data, and any user defined widths,
5324                                 * then insert it into the DOM and allow the browser to do all the hard work of
5325                                 * calculating table widths.
5326                                 */
5327                                var
5328                                        nCalcTmp = oSettings.nTable.cloneNode( false ),
5329                                        nBody = document.createElement( 'tbody' ),
5330                                        nTr = document.createElement( 'tr' ),
5331                                        nDivSizing;
5332                               
5333                                nCalcTmp.removeAttribute( "id" );
5334                                nCalcTmp.appendChild( oSettings.nTHead.cloneNode(true) );
5335                                if ( oSettings.nTFoot !== null )
5336                                {
5337                                        nCalcTmp.appendChild( oSettings.nTFoot.cloneNode(true) );
5338                                        _fnApplyToChildren( function(n) {
5339                                                n.style.width = "";
5340                                        }, nCalcTmp.getElementsByTagName('tr') );
5341                                }
5342                               
5343                                nCalcTmp.appendChild( nBody );
5344                                nBody.appendChild( nTr );
5345                               
5346                                /* Remove any sizing that was previously applied by the styles */
5347                                var jqColSizing = $('thead th', nCalcTmp);
5348                                if ( jqColSizing.length === 0 )
5349                                {
5350                                        jqColSizing = $('tbody tr:eq(0)>td', nCalcTmp);
5351                                }
5352                                jqColSizing.each( function (i) {
5353                                        this.style.width = "";
5354                                       
5355                                        var iIndex = _fnVisibleToColumnIndex( oSettings, i );
5356                                        if ( iIndex !== null && oSettings.aoColumns[iIndex].sWidthOrig !== "" )
5357                                        {
5358                                                this.style.width = oSettings.aoColumns[iIndex].sWidthOrig;
5359                                        }
5360                                } );
5361                               
5362                                /* Find the biggest td for each column and put it into the table */
5363                                for ( i=0 ; i<iColums ; i++ )
5364                                {
5365                                        if ( oSettings.aoColumns[i].bVisible )
5366                                        {
5367                                                var nTd = _fnGetWidestNode( oSettings, i );
5368                                                if ( nTd !== null )
5369                                                {
5370                                                        nTd = nTd.cloneNode(true);
5371                                                        nTr.appendChild( nTd );
5372                                                }
5373                                        }
5374                                }
5375                               
5376                                /* Build the table and 'display' it */
5377                                var nWrapper = oSettings.nTable.parentNode;
5378                                nWrapper.appendChild( nCalcTmp );
5379                               
5380                                /* When scrolling (X or Y) we want to set the width of the table as appropriate. However,
5381                                 * when not scrolling leave the table width as it is. This results in slightly different,
5382                                 * but I think correct behaviour
5383                                 */
5384                                if ( oSettings.oScroll.sX !== "" && oSettings.oScroll.sXInner !== "" )
5385                                {
5386                                        nCalcTmp.style.width = _fnStringToCss(oSettings.oScroll.sXInner);
5387                                }
5388                                else if ( oSettings.oScroll.sX !== "" )
5389                                {
5390                                        nCalcTmp.style.width = "";
5391                                        if ( $(nCalcTmp).width() < nWrapper.offsetWidth )
5392                                        {
5393                                                nCalcTmp.style.width = _fnStringToCss( nWrapper.offsetWidth );
5394                                        }
5395                                }
5396                                else if ( oSettings.oScroll.sY !== "" )
5397                                {
5398                                        nCalcTmp.style.width = _fnStringToCss( nWrapper.offsetWidth );
5399                                }
5400                                nCalcTmp.style.visibility = "hidden";
5401                               
5402                                /* Scrolling considerations */
5403                                _fnScrollingWidthAdjust( oSettings, nCalcTmp );
5404                               
5405                                /* Read the width's calculated by the browser and store them for use by the caller. We
5406                                 * first of all try to use the elements in the body, but it is possible that there are
5407                                 * no elements there, under which circumstances we use the header elements
5408                                 */
5409                                var oNodes = $("tbody tr:eq(0)>td", nCalcTmp);
5410                                if ( oNodes.length === 0 )
5411                                {
5412                                        oNodes = $("thead tr:eq(0)>th", nCalcTmp);
5413                                }
5414                               
5415                                var iIndex, iCorrector = 0, iWidth;
5416                                for ( i=0 ; i<oSettings.aoColumns.length ; i++ )
5417                                {
5418                                        if ( oSettings.aoColumns[i].bVisible )
5419                                        {
5420                                                iWidth = $(oNodes[iCorrector]).outerWidth();
5421                                                if ( iWidth !== null && iWidth > 0 )
5422                                                {
5423                                                        oSettings.aoColumns[i].sWidth = _fnStringToCss( iWidth );
5424                                                }
5425                                                iCorrector++;
5426                                        }
5427                                }
5428                               
5429                                oSettings.nTable.style.width = _fnStringToCss( $(nCalcTmp).outerWidth() );
5430                                nCalcTmp.parentNode.removeChild( nCalcTmp );
5431                        }
5432                }
5433               
5434                /*
5435                 * Function: _fnScrollingWidthAdjust
5436                 * Purpose:  Adjust a table's width to take account of scrolling
5437                 * Returns:  -
5438                 * Inputs:   object:oSettings - dataTables settings object
5439                 *           node:n - table node
5440                 */
5441                function _fnScrollingWidthAdjust ( oSettings, n )
5442                {
5443                        if ( oSettings.oScroll.sX === "" && oSettings.oScroll.sY !== "" )
5444                        {
5445                                /* When y-scrolling only, we want to remove the width of the scroll bar so the table
5446                                 * + scroll bar will fit into the area avaialble.
5447                                 */
5448                                var iOrigWidth = $(n).width();
5449                                n.style.width = _fnStringToCss( $(n).outerWidth()-oSettings.oScroll.iBarWidth );
5450                        }
5451                        else if ( oSettings.oScroll.sX !== "" )
5452                        {
5453                                /* When x-scrolling both ways, fix the table at it's current size, without adjusting */
5454                                n.style.width = _fnStringToCss( $(n).outerWidth() );
5455                        }
5456                }
5457               
5458                /*
5459                 * Function: _fnGetWidestNode
5460                 * Purpose:  Get the widest node
5461                 * Returns:  string: - max strlens for each column
5462                 * Inputs:   object:oSettings - dataTables settings object
5463                 *           int:iCol - column of interest
5464                 *           boolean:bFast - Should we use fast (but non-accurate) calculation - optional,
5465                 *             default true
5466                 * Notes:    This operation is _expensive_ (!!!). It requires a lot of DOM interaction, but
5467                 *   this is the only way to reliably get the widest string. For example 'mmm' would be wider
5468                 *   than 'iiii' so we can't just ocunt characters. If this can be optimised it would be good
5469                 *   to do so!
5470                 */
5471                function _fnGetWidestNode( oSettings, iCol, bFast )
5472                {
5473                        /* Use fast not non-accurate calculate based on the strlen */
5474                        if ( typeof bFast == 'undefined' || bFast )
5475                        {
5476                                var iMaxLen = _fnGetMaxLenString( oSettings, iCol );
5477                                var iFastVis = _fnColumnIndexToVisible( oSettings, iCol);
5478                                if ( iMaxLen < 0 )
5479                                {
5480                                        return null;
5481                                }
5482                                return oSettings.aoData[iMaxLen].nTr.getElementsByTagName('td')[iFastVis];
5483                        }
5484                       
5485                        /* Use the slow approach, but get high quality answers - note that this code is not actually
5486                         * used by DataTables by default. If you want to use it you can alter the call to
5487                         * _fnGetWidestNode to pass 'false' as the third argument
5488                         */
5489                        var
5490                                iMax = -1, i, iLen,
5491                                iMaxIndex = -1,
5492                                n = document.createElement('div');
5493                       
5494                        n.style.visibility = "hidden";
5495                        n.style.position = "absolute";
5496                        document.body.appendChild( n );
5497                       
5498                        for ( i=0, iLen=oSettings.aoData.length ; i<iLen ; i++ )
5499                        {
5500                                n.innerHTML = oSettings.aoData[i]._aData[iCol];
5501                                if ( n.offsetWidth > iMax )
5502                                {
5503                                        iMax = n.offsetWidth;
5504                                        iMaxIndex = i;
5505                                }
5506                        }
5507                        document.body.removeChild( n );
5508                       
5509                        if ( iMaxIndex >= 0 )
5510                        {
5511                                var iVis = _fnColumnIndexToVisible( oSettings, iCol);
5512                                var nRet = oSettings.aoData[iMaxIndex].nTr.getElementsByTagName('td')[iVis];
5513                                if ( nRet )
5514                                {
5515                                        return nRet;
5516                                }
5517                        }
5518                        return null;
5519                }
5520               
5521                /*
5522                 * Function: _fnGetMaxLenString
5523                 * Purpose:  Get the maximum strlen for each data column
5524                 * Returns:  string: - max strlens for each column
5525                 * Inputs:   object:oSettings - dataTables settings object
5526                 *           int:iCol - column of interest
5527                 */
5528                function _fnGetMaxLenString( oSettings, iCol )
5529                {
5530                        var iMax = -1;
5531                        var iMaxIndex = -1;
5532                       
5533                        for ( var i=0 ; i<oSettings.aoData.length ; i++ )
5534                        {
5535                                var s = oSettings.aoData[i]._aData[iCol];
5536                                if ( s.length > iMax )
5537                                {
5538                                        iMax = s.length;
5539                                        iMaxIndex = i;
5540                                }
5541                        }
5542                       
5543                        return iMaxIndex;
5544                }
5545               
5546                /*
5547                 * Function: _fnStringToCss
5548                 * Purpose:  Append a CSS unit (only if required) to a string
5549                 * Returns:  0 if match, 1 if length is different, 2 if no match
5550                 * Inputs:   array:aArray1 - first array
5551                 *           array:aArray2 - second array
5552                 */
5553                function _fnStringToCss( s )
5554                {
5555                        if ( s === null )
5556                        {
5557                                return "0px";
5558                        }
5559                       
5560                        if ( typeof s == 'number' )
5561                        {
5562                                if ( s < 0 )
5563                                {
5564                                        return "0px";
5565                                }
5566                                return s+"px";
5567                        }
5568                       
5569                        /* Check if the last character is not 0-9 */
5570                        var c = s.charCodeAt( s.length-1 );
5571                        if (c < 0x30 || c > 0x39)
5572                        {
5573                                return s;
5574                        }
5575                        return s+"px";
5576                }
5577               
5578                /*
5579                 * Function: _fnArrayCmp
5580                 * Purpose:  Compare two arrays
5581                 * Returns:  0 if match, 1 if length is different, 2 if no match
5582                 * Inputs:   array:aArray1 - first array
5583                 *           array:aArray2 - second array
5584                 */
5585                function _fnArrayCmp( aArray1, aArray2 )
5586                {
5587                        if ( aArray1.length != aArray2.length )
5588                        {
5589                                return 1;
5590                        }
5591                       
5592                        for ( var i=0 ; i<aArray1.length ; i++ )
5593                        {
5594                                if ( aArray1[i] != aArray2[i] )
5595                                {
5596                                        return 2;
5597                                }
5598                        }
5599                       
5600                        return 0;
5601                }
5602               
5603                /*
5604                 * Function: _fnDetectType
5605                 * Purpose:  Get the sort type based on an input string
5606                 * Returns:  string: - type (defaults to 'string' if no type can be detected)
5607                 * Inputs:   string:sData - data we wish to know the type of
5608                 * Notes:    This function makes use of the DataTables plugin objct _oExt
5609                 *   (.aTypes) such that new types can easily be added.
5610                 */
5611                function _fnDetectType( sData )
5612                {
5613                        var aTypes = _oExt.aTypes;
5614                        var iLen = aTypes.length;
5615                       
5616                        for ( var i=0 ; i<iLen ; i++ )
5617                        {
5618                                var sType = aTypes[i]( sData );
5619                                if ( sType !== null )
5620                                {
5621                                        return sType;
5622                                }
5623                        }
5624                       
5625                        return 'string';
5626                }
5627               
5628                /*
5629                 * Function: _fnSettingsFromNode
5630                 * Purpose:  Return the settings object for a particular table
5631                 * Returns:  object: Settings object - or null if not found
5632                 * Inputs:   node:nTable - table we are using as a dataTable
5633                 */
5634                function _fnSettingsFromNode ( nTable )
5635                {
5636                        for ( var i=0 ; i<_aoSettings.length ; i++ )
5637                        {
5638                                if ( _aoSettings[i].nTable == nTable )
5639                                {
5640                                        return _aoSettings[i];
5641                                }
5642                        }
5643                       
5644                        return null;
5645                }
5646               
5647                /*
5648                 * Function: _fnGetDataMaster
5649                 * Purpose:  Return an array with the full table data
5650                 * Returns:  array array:aData - Master data array
5651                 * Inputs:   object:oSettings - dataTables settings object
5652                 */
5653                function _fnGetDataMaster ( oSettings )
5654                {
5655                        var aData = [];
5656                        var iLen = oSettings.aoData.length;
5657                        for ( var i=0 ; i<iLen; i++ )
5658                        {
5659                                aData.push( oSettings.aoData[i]._aData );
5660                        }
5661                        return aData;
5662                }
5663               
5664                /*
5665                 * Function: _fnGetTrNodes
5666                 * Purpose:  Return an array with the TR nodes for the table
5667                 * Returns:  array: - TR array
5668                 * Inputs:   object:oSettings - dataTables settings object
5669                 */
5670                function _fnGetTrNodes ( oSettings )
5671                {
5672                        var aNodes = [];
5673                        var iLen = oSettings.aoData.length;
5674                        for ( var i=0 ; i<iLen ; i++ )
5675                        {
5676                                aNodes.push( oSettings.aoData[i].nTr );
5677                        }
5678                        return aNodes;
5679                }
5680               
5681                /*
5682                 * Function: _fnGetTdNodes
5683                 * Purpose:  Return an array with the TD nodes for the table
5684                 * Returns:  array: - TD array
5685                 * Inputs:   object:oSettings - dataTables settings object
5686                 */
5687                function _fnGetTdNodes ( oSettings )
5688                {
5689                        var nTrs = _fnGetTrNodes( oSettings );
5690                        var nTds = [], nTd;
5691                        var anReturn = [];
5692                        var iCorrector;
5693                        var iRow, iRows, iColumn, iColumns;
5694                       
5695                        for ( iRow=0, iRows=nTrs.length ; iRow<iRows ; iRow++ )
5696                        {
5697                                nTds = [];
5698                                for ( iColumn=0, iColumns=nTrs[iRow].childNodes.length ; iColumn<iColumns ; iColumn++ )
5699                                {
5700                                        nTd = nTrs[iRow].childNodes[iColumn];
5701                                        if ( nTd.nodeName.toUpperCase() == "TD" )
5702                                        {
5703                                                nTds.push( nTd );
5704                                        }
5705                                }
5706                               
5707                                iCorrector = 0;
5708                                for ( iColumn=0, iColumns=oSettings.aoColumns.length ; iColumn<iColumns ; iColumn++ )
5709                                {
5710                                        if ( oSettings.aoColumns[iColumn].bVisible )
5711                                        {
5712                                                anReturn.push( nTds[iColumn-iCorrector] );
5713                                        }
5714                                        else
5715                                        {
5716                                                anReturn.push( oSettings.aoData[iRow]._anHidden[iColumn] );
5717                                                iCorrector++;
5718                                        }
5719                                }
5720                        }
5721                        return anReturn;
5722                }
5723               
5724                /*
5725                 * Function: _fnEscapeRegex
5726                 * Purpose:  scape a string stuch that it can be used in a regular expression
5727                 * Returns:  string: - escaped string
5728                 * Inputs:   string:sVal - string to escape
5729                 */
5730                function _fnEscapeRegex ( sVal )
5731                {
5732                        var acEscape = [ '/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\', '$', '^' ];
5733                  var reReplace = new RegExp( '(\\' + acEscape.join('|\\') + ')', 'g' );
5734                  return sVal.replace(reReplace, '\\$1');
5735                }
5736               
5737                /*
5738                 * Function: _fnDeleteIndex
5739                 * Purpose:  Take an array of integers (index array) and remove a target integer (value - not
5740                 *             the key!)
5741                 * Returns:  -
5742                 * Inputs:   a:array int - Index array to target
5743                 *           int:iTarget - value to find
5744                 */
5745                function _fnDeleteIndex( a, iTarget )
5746                {
5747                        var iTargetIndex = -1;
5748                       
5749                        for ( var i=0, iLen=a.length ; i<iLen ; i++ )
5750                        {
5751                                if ( a[i] == iTarget )
5752                                {
5753                                        iTargetIndex = i;
5754                                }
5755                                else if ( a[i] > iTarget )
5756                                {
5757                                        a[i]--;
5758                                }
5759                        }
5760                       
5761                        if ( iTargetIndex != -1 )
5762                        {
5763                                a.splice( iTargetIndex, 1 );
5764                        }
5765                }
5766               
5767                /*
5768                 * Function: _fnReOrderIndex
5769                 * Purpose:  Figure out how to reorder a display list
5770                 * Returns:  array int:aiReturn - index list for reordering
5771                 * Inputs:   object:oSettings - dataTables settings object
5772                 */
5773                function _fnReOrderIndex ( oSettings, sColumns )
5774                {
5775                        var aColumns = sColumns.split(',');
5776                        var aiReturn = [];
5777                       
5778                        for ( var i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
5779                        {
5780                                for ( var j=0 ; j<iLen ; j++ )
5781                                {
5782                                        if ( oSettings.aoColumns[i].sName == aColumns[j] )
5783                                        {
5784                                                aiReturn.push( j );
5785                                                break;
5786                                        }
5787                                }
5788                        }
5789                       
5790                        return aiReturn;
5791                }
5792               
5793                /*
5794                 * Function: _fnColumnOrdering
5795                 * Purpose:  Get the column ordering that DataTables expects
5796                 * Returns:  string: - comma separated list of names
5797                 * Inputs:   object:oSettings - dataTables settings object
5798                 */
5799                function _fnColumnOrdering ( oSettings )
5800                {
5801                        var sNames = '';
5802                        for ( var i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
5803                        {
5804                                sNames += oSettings.aoColumns[i].sName+',';
5805                        }
5806                        if ( sNames.length == iLen )
5807                        {
5808                                return "";
5809                        }
5810                        return sNames.slice(0, -1);
5811                }
5812               
5813                /*
5814                 * Function: _fnLog
5815                 * Purpose:  Log an error message
5816                 * Returns:  -
5817                 * Inputs:   int:iLevel - log error messages, or display them to the user
5818                 *           string:sMesg - error message
5819                 */
5820                function _fnLog( oSettings, iLevel, sMesg )
5821                {
5822                        var sAlert = oSettings.sTableId === "" ?
5823                                "DataTables warning: " +sMesg :
5824                                "DataTables warning (table id = '"+oSettings.sTableId+"'): " +sMesg;
5825                       
5826                        if ( iLevel === 0 )
5827                        {
5828                                if ( _oExt.sErrMode == 'alert' )
5829                                {
5830                                        alert( sAlert );
5831                                }
5832                                else
5833                                {
5834                                        throw sAlert;
5835                                }
5836                                return;
5837                        }
5838                        else if ( typeof console != 'undefined' && typeof console.log != 'undefined' )
5839                        {
5840                                console.log( sAlert );
5841                        }
5842                }
5843               
5844                /*
5845                 * Function: _fnClearTable
5846                 * Purpose:  Nuke the table
5847                 * Returns:  -
5848                 * Inputs:   object:oSettings - dataTables settings object
5849                 */
5850                function _fnClearTable( oSettings )
5851                {
5852                        oSettings.aoData.splice( 0, oSettings.aoData.length );
5853                        oSettings.aiDisplayMaster.splice( 0, oSettings.aiDisplayMaster.length );
5854                        oSettings.aiDisplay.splice( 0, oSettings.aiDisplay.length );
5855                        _fnCalculateEnd( oSettings );
5856                }
5857               
5858                /*
5859                 * Function: _fnSaveState
5860                 * Purpose:  Save the state of a table in a cookie such that the page can be reloaded
5861                 * Returns:  -
5862                 * Inputs:   object:oSettings - dataTables settings object
5863                 */
5864                function _fnSaveState ( oSettings )
5865                {
5866                        if ( !oSettings.oFeatures.bStateSave || typeof oSettings.bDestroying != 'undefined' )
5867                        {
5868                                return;
5869                        }
5870                       
5871                        /* Store the interesting variables */
5872                        var i, iLen, sTmp;
5873                        var sValue = "{";
5874                        sValue += '"iCreate":'+ new Date().getTime()+',';
5875                        sValue += '"iStart":'+ oSettings._iDisplayStart+',';
5876                        sValue += '"iEnd":'+ oSettings._iDisplayEnd+',';
5877                        sValue += '"iLength":'+ oSettings._iDisplayLength+',';
5878                        sValue += '"sFilter":"'+ encodeURIComponent(oSettings.oPreviousSearch.sSearch)+'",';
5879                        sValue += '"sFilterEsc":'+ !oSettings.oPreviousSearch.bRegex+',';
5880                       
5881                        sValue += '"aaSorting":[ ';
5882                        for ( i=0 ; i<oSettings.aaSorting.length ; i++ )
5883                        {
5884                                sValue += '['+oSettings.aaSorting[i][0]+',"'+oSettings.aaSorting[i][1]+'"],';
5885                        }
5886                        sValue = sValue.substring(0, sValue.length-1);
5887                        sValue += "],";
5888                       
5889                        sValue += '"aaSearchCols":[ ';
5890                        for ( i=0 ; i<oSettings.aoPreSearchCols.length ; i++ )
5891                        {
5892                                sValue += '["'+encodeURIComponent(oSettings.aoPreSearchCols[i].sSearch)+
5893                                        '",'+!oSettings.aoPreSearchCols[i].bRegex+'],';
5894                        }
5895                        sValue = sValue.substring(0, sValue.length-1);
5896                        sValue += "],";
5897                       
5898                        sValue += '"abVisCols":[ ';
5899                        for ( i=0 ; i<oSettings.aoColumns.length ; i++ )
5900                        {
5901                                sValue += oSettings.aoColumns[i].bVisible+",";
5902                        }
5903                        sValue = sValue.substring(0, sValue.length-1);
5904                        sValue += "]";
5905                       
5906                        /* Save state from any plug-ins */
5907                        for ( i=0, iLen=oSettings.aoStateSave.length ; i<iLen ; i++ )
5908                        {
5909                                sTmp = oSettings.aoStateSave[i].fn( oSettings, sValue );
5910                                if ( sTmp !== "" )
5911                                {
5912                                        sValue = sTmp;
5913                                }
5914                        }
5915                       
5916                        sValue += "}";
5917                       
5918                        _fnCreateCookie( oSettings.sCookiePrefix+oSettings.sInstance, sValue,
5919                                oSettings.iCookieDuration, oSettings.sCookiePrefix, oSettings.fnCookieCallback );
5920                }
5921               
5922                /*
5923                 * Function: _fnLoadState
5924                 * Purpose:  Attempt to load a saved table state from a cookie
5925                 * Returns:  -
5926                 * Inputs:   object:oSettings - dataTables settings object
5927                 *           object:oInit - DataTables init object so we can override settings
5928                 */
5929                function _fnLoadState ( oSettings, oInit )
5930                {
5931                        if ( !oSettings.oFeatures.bStateSave )
5932                        {
5933                                return;
5934                        }
5935                       
5936                        var oData, i, iLen;
5937                        var sData = _fnReadCookie( oSettings.sCookiePrefix+oSettings.sInstance );
5938                        if ( sData !== null && sData !== '' )
5939                        {
5940                                /* Try/catch the JSON eval - if it is bad then we ignore it - note that 1.7.0 and before
5941                                 * incorrectly used single quotes for some strings - hence the replace below
5942                                 */
5943                                try
5944                                {
5945                                        oData = (typeof $.parseJSON == 'function') ?
5946                                                $.parseJSON( sData.replace(/'/g, '"') ) : eval( '('+sData+')' );
5947                                }
5948                                catch( e )
5949                                {
5950                                        return;
5951                                }
5952                               
5953                                /* Allow custom and plug-in manipulation functions to alter the data set which was
5954                                 * saved, and also reject any saved state by returning false
5955                                 */
5956                                for ( i=0, iLen=oSettings.aoStateLoad.length ; i<iLen ; i++ )
5957                                {
5958                                        if ( !oSettings.aoStateLoad[i].fn( oSettings, oData ) )
5959                                        {
5960                                                return;
5961                                        }
5962                                }
5963                               
5964                                /* Store the saved state so it might be accessed at any time (particualrly a plug-in */
5965                                oSettings.oLoadedState = $.extend( true, {}, oData );
5966                               
5967                                /* Restore key features */
5968                                oSettings._iDisplayStart = oData.iStart;
5969                                oSettings.iInitDisplayStart = oData.iStart;
5970                                oSettings._iDisplayEnd = oData.iEnd;
5971                                oSettings._iDisplayLength = oData.iLength;
5972                                oSettings.oPreviousSearch.sSearch = decodeURIComponent(oData.sFilter);
5973                                oSettings.aaSorting = oData.aaSorting.slice();
5974                                oSettings.saved_aaSorting = oData.aaSorting.slice();
5975                               
5976                                /*
5977                                 * Search filtering - global reference added in 1.4.1
5978                                 * Note that we use a 'not' for the value of the regular expression indicator to maintain
5979                                 * compatibility with pre 1.7 versions, where this was basically inverted. Added in 1.7.0
5980                                 */
5981                                if ( typeof oData.sFilterEsc != 'undefined' )
5982                                {
5983                                        oSettings.oPreviousSearch.bRegex = !oData.sFilterEsc;
5984                                }
5985                               
5986                                /* Column filtering - added in 1.5.0 beta 6 */
5987                                if ( typeof oData.aaSearchCols != 'undefined' )
5988                                {
5989                                        for ( i=0 ; i<oData.aaSearchCols.length ; i++ )
5990                                        {
5991                                                oSettings.aoPreSearchCols[i] = {
5992                                                        "sSearch": decodeURIComponent(oData.aaSearchCols[i][0]),
5993                                                        "bRegex": !oData.aaSearchCols[i][1]
5994                                                };
5995                                        }
5996                                }
5997                               
5998                                /* Column visibility state - added in 1.5.0 beta 10 */
5999                                if ( typeof oData.abVisCols != 'undefined' )
6000                                {
6001                                        /* Pass back visibiliy settings to the init handler, but to do not here override
6002                                         * the init object that the user might have passed in
6003                                         */
6004                                        oInit.saved_aoColumns = [];
6005                                        for ( i=0 ; i<oData.abVisCols.length ; i++ )
6006                                        {
6007                                                oInit.saved_aoColumns[i] = {};
6008                                                oInit.saved_aoColumns[i].bVisible = oData.abVisCols[i];
6009                                        }
6010                                }
6011                        }
6012                }
6013               
6014                /*
6015                 * Function: _fnCreateCookie
6016                 * Purpose:  Create a new cookie with a value to store the state of a table
6017                 * Returns:  -
6018                 * Inputs:   string:sName - name of the cookie to create
6019                 *           string:sValue - the value the cookie should take
6020                 *           int:iSecs - duration of the cookie
6021                 *           string:sBaseName - sName is made up of the base + file name - this is the base
6022                 *           function:fnCallback - User definable function to modify the cookie
6023                 */
6024                function _fnCreateCookie ( sName, sValue, iSecs, sBaseName, fnCallback )
6025                {
6026                        var date = new Date();
6027                        date.setTime( date.getTime()+(iSecs*1000) );
6028                       
6029                        /*
6030                         * Shocking but true - it would appear IE has major issues with having the path not having
6031                         * a trailing slash on it. We need the cookie to be available based on the path, so we
6032                         * have to append the file name to the cookie name. Appalling. Thanks to vex for adding the
6033                         * patch to use at least some of the path
6034                         */
6035                        var aParts = window.location.pathname.split('/');
6036                        var sNameFile = sName + '_' + aParts.pop().replace(/[\/:]/g,"").toLowerCase();
6037                        var sFullCookie, oData;
6038                       
6039                        if ( fnCallback !== null )
6040                        {
6041                                oData = (typeof $.parseJSON == 'function') ?
6042                                        $.parseJSON( sValue ) : eval( '('+sValue+')' );
6043                                sFullCookie = fnCallback( sNameFile, oData, date.toGMTString(),
6044                                        aParts.join('/')+"/" );
6045                        }
6046                        else
6047                        {
6048                                sFullCookie = sNameFile + "=" + encodeURIComponent(sValue) +
6049                                        "; expires=" + date.toGMTString() +"; path=" + aParts.join('/')+"/";
6050                        }
6051                       
6052                        /* Are we going to go over the cookie limit of 4KiB? If so, try to delete a cookies
6053                         * belonging to DataTables. This is FAR from bullet proof
6054                         */
6055                        var sOldName="", iOldTime=9999999999999;
6056                        var iLength = _fnReadCookie( sNameFile )!==null ? document.cookie.length :
6057                                sFullCookie.length + document.cookie.length;
6058                       
6059                        if ( iLength+10 > 4096 ) /* Magic 10 for padding */
6060                        {
6061                                var aCookies =document.cookie.split(';');
6062                                for ( var i=0, iLen=aCookies.length ; i<iLen ; i++ )
6063                                {
6064                                        if ( aCookies[i].indexOf( sBaseName ) != -1 )
6065                                        {
6066                                                /* It's a DataTables cookie, so eval it and check the time stamp */
6067                                                var aSplitCookie = aCookies[i].split('=');
6068                                                try { oData = eval( '('+decodeURIComponent(aSplitCookie[1])+')' ); }
6069                                                catch( e ) { continue; }
6070                                               
6071                                                if ( typeof oData.iCreate != 'undefined' && oData.iCreate < iOldTime )
6072                                                {
6073                                                        sOldName = aSplitCookie[0];
6074                                                        iOldTime = oData.iCreate;
6075                                                }
6076                                        }
6077                                }
6078                               
6079                                if ( sOldName !== "" )
6080                                {
6081                                        document.cookie = sOldName+"=; expires=Thu, 01-Jan-1970 00:00:01 GMT; path="+
6082                                                aParts.join('/') + "/";
6083                                }
6084                        }
6085                       
6086                        document.cookie = sFullCookie;
6087                }
6088               
6089                /*
6090                 * Function: _fnReadCookie
6091                 * Purpose:  Read an old cookie to get a cookie with an old table state
6092                 * Returns:  string: - contents of the cookie - or null if no cookie with that name found
6093                 * Inputs:   string:sName - name of the cookie to read
6094                 */
6095                function _fnReadCookie ( sName )
6096                {
6097                        var
6098                                aParts = window.location.pathname.split('/'),
6099                                sNameEQ = sName + '_' + aParts[aParts.length-1].replace(/[\/:]/g,"").toLowerCase() + '=',
6100                                sCookieContents = document.cookie.split(';');
6101                       
6102                        for( var i=0 ; i<sCookieContents.length ; i++ )
6103                        {
6104                                var c = sCookieContents[i];
6105                               
6106                                while (c.charAt(0)==' ')
6107                                {
6108                                        c = c.substring(1,c.length);
6109                                }
6110                               
6111                                if (c.indexOf(sNameEQ) === 0)
6112                                {
6113                                        return decodeURIComponent( c.substring(sNameEQ.length,c.length) );
6114                                }
6115                        }
6116                        return null;
6117                }
6118               
6119                /*
6120                 * Function: _fnGetUniqueThs
6121                 * Purpose:  Get an array of unique th elements, one for each column
6122                 * Returns:  array node:aReturn - list of unique ths
6123                 * Inputs:   node:nThead - The thead element for the table
6124                 */
6125                function _fnGetUniqueThs ( nThead )
6126                {
6127                        var nTrs = nThead.getElementsByTagName('tr');
6128                       
6129                        /* Nice simple case */
6130                        if ( nTrs.length == 1 )
6131                        {
6132                                return nTrs[0].getElementsByTagName('th');
6133                        }
6134                       
6135                        /* Otherwise we need to figure out the layout array to get the nodes */
6136                        var aLayout = [], aReturn = [];
6137                        var ROWSPAN = 2, COLSPAN = 3, TDELEM = 4;
6138                        var i, j, k, iLen, jLen, iColumnShifted;
6139                        var fnShiftCol = function ( a, i, j ) {
6140                                while ( typeof a[i][j] != 'undefined' ) {
6141                                        j++;
6142                                }
6143                                return j;
6144                        };
6145                        var fnAddRow = function ( i ) {
6146                                if ( typeof aLayout[i] == 'undefined' ) {
6147                                        aLayout[i] = [];
6148                                }
6149                        };
6150                       
6151                        /* Calculate a layout array */
6152                        for ( i=0, iLen=nTrs.length ; i<iLen ; i++ )
6153                        {
6154                                fnAddRow( i );
6155                                var iColumn = 0;
6156                                var nTds = [];
6157                               
6158                                for ( j=0, jLen=nTrs[i].childNodes.length ; j<jLen ; j++ )
6159                                {
6160                                        if ( nTrs[i].childNodes[j].nodeName.toUpperCase() == "TD" ||
6161                                             nTrs[i].childNodes[j].nodeName.toUpperCase() == "TH" )
6162                                        {
6163                                                nTds.push( nTrs[i].childNodes[j] );
6164                                        }
6165                                }
6166                               
6167                                for ( j=0, jLen=nTds.length ; j<jLen ; j++ )
6168                                {
6169                                        var iColspan = nTds[j].getAttribute('colspan') * 1;
6170                                        var iRowspan = nTds[j].getAttribute('rowspan') * 1;
6171                                       
6172                                        if ( !iColspan || iColspan===0 || iColspan===1 )
6173                                        {
6174                                                iColumnShifted = fnShiftCol( aLayout, i, iColumn );
6175                                                aLayout[i][iColumnShifted] = (nTds[j].nodeName.toUpperCase()=="TD") ? TDELEM : nTds[j];
6176                                                if ( iRowspan || iRowspan===0 || iRowspan===1 )
6177                                                {
6178                                                        for ( k=1 ; k<iRowspan ; k++ )
6179                                                        {
6180                                                                fnAddRow( i+k );
6181                                                                aLayout[i+k][iColumnShifted] = ROWSPAN;
6182                                                        }
6183                                                }
6184                                                iColumn++;
6185                                        }
6186                                        else
6187                                        {
6188                                                iColumnShifted = fnShiftCol( aLayout, i, iColumn );
6189                                                for ( k=0 ; k<iColspan ; k++ )
6190                                                {
6191                                                        aLayout[i][iColumnShifted+k] = COLSPAN;
6192                                                }
6193                                                iColumn += iColspan;
6194                                        }
6195                                }
6196                        }
6197                       
6198                        /* Convert the layout array into a node array */
6199                        for ( i=0, iLen=aLayout.length ; i<iLen ; i++ )
6200                        {
6201                                for ( j=0, jLen=aLayout[i].length ; j<jLen ; j++ )
6202                                {
6203                                        if ( typeof aLayout[i][j] == 'object' && typeof aReturn[j] == 'undefined' )
6204                                        {
6205                                                aReturn[j] = aLayout[i][j];
6206                                        }
6207                                }
6208                        }
6209                       
6210                        return aReturn;
6211                }
6212               
6213                /*
6214                 * Function: _fnScrollBarWidth
6215                 * Purpose:  Get the width of a scroll bar in this browser being used
6216                 * Returns:  int: - width in pixels
6217                 * Inputs:   -
6218                 * Notes:    All credit for this function belongs to Alexandre Gomes. Thanks for sharing!
6219                 *   http://www.alexandre-gomes.com/?p=115
6220                 */
6221                function _fnScrollBarWidth ()
6222                { 
6223                        var inner = document.createElement('p'); 
6224                        var style = inner.style;
6225                        style.width = "100%"; 
6226                        style.height = "200px"; 
6227                       
6228                        var outer = document.createElement('div'); 
6229                        style = outer.style;
6230                        style.position = "absolute"; 
6231                        style.top = "0px"; 
6232                        style.left = "0px"; 
6233                        style.visibility = "hidden"; 
6234                        style.width = "200px"; 
6235                        style.height = "150px"; 
6236                        style.overflow = "hidden"; 
6237                        outer.appendChild(inner); 
6238                       
6239                        document.body.appendChild(outer); 
6240                        var w1 = inner.offsetWidth; 
6241                        outer.style.overflow = 'scroll'; 
6242                        var w2 = inner.offsetWidth; 
6243                        if ( w1 == w2 )
6244                        {
6245                                w2 = outer.clientWidth; 
6246                        }
6247                       
6248                        document.body.removeChild(outer);
6249                        return (w1 - w2); 
6250                }
6251               
6252                /*
6253                 * Function: _fnApplyToChildren
6254                 * Purpose:  Apply a given function to the display child nodes of an element array (typically
6255                 *   TD children of TR rows
6256                 * Returns:  - (done by reference)
6257                 * Inputs:   function:fn - Method to apply to the objects
6258                 *           array nodes:an1 - List of elements to look through for display children
6259                 *           array nodes:an2 - Another list (identical structure to the first) - optional
6260                 */
6261                function _fnApplyToChildren( fn, an1, an2 )
6262                {
6263                        for ( var i=0, iLen=an1.length ; i<iLen ; i++ )
6264                        {
6265                                for ( var j=0, jLen=an1[i].childNodes.length ; j<jLen ; j++ )
6266                                {
6267                                        if ( an1[i].childNodes[j].nodeType == 1 )
6268                                        {
6269                                                if ( typeof an2 != 'undefined' )
6270                                                {
6271                                                        fn( an1[i].childNodes[j], an2[i].childNodes[j] );
6272                                                }
6273                                                else
6274                                                {
6275                                                        fn( an1[i].childNodes[j] );
6276                                                }
6277                                        }
6278                                }
6279                        }
6280                }
6281               
6282                /*
6283                 * Function: _fnMap
6284                 * Purpose:  See if a property is defined on one object, if so assign it to the other object
6285                 * Returns:  - (done by reference)
6286                 * Inputs:   object:oRet - target object
6287                 *           object:oSrc - source object
6288                 *           string:sName - property
6289                 *           string:sMappedName - name to map too - optional, sName used if not given
6290                 */
6291                function _fnMap( oRet, oSrc, sName, sMappedName )
6292                {
6293                        if ( typeof sMappedName == 'undefined' )
6294                        {
6295                                sMappedName = sName;
6296                        }
6297                        if ( typeof oSrc[sName] != 'undefined' )
6298                        {
6299                                oRet[sMappedName] = oSrc[sName];
6300                        }
6301                }
6302               
6303                /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6304                 * Section - API
6305                 *
6306                 * I'm not overly happy with this solution - I'd much rather that there was a way of getting
6307                 * a list of all the private functions and do what we need to dynamically - but that doesn't
6308                 * appear to be possible. Bonkers. A better solution would be to provide a 'bind' type object
6309                 * To do - bind type method in DTs 2.x.
6310                 */
6311                this.oApi._fnExternApiFunc = _fnExternApiFunc;
6312                this.oApi._fnInitalise = _fnInitalise;
6313                this.oApi._fnLanguageProcess = _fnLanguageProcess;
6314                this.oApi._fnAddColumn = _fnAddColumn;
6315                this.oApi._fnColumnOptions = _fnColumnOptions;
6316                this.oApi._fnAddData = _fnAddData;
6317                this.oApi._fnGatherData = _fnGatherData;
6318                this.oApi._fnDrawHead = _fnDrawHead;
6319                this.oApi._fnDraw = _fnDraw;
6320                this.oApi._fnReDraw = _fnReDraw;
6321                this.oApi._fnAjaxUpdate = _fnAjaxUpdate;
6322                this.oApi._fnAjaxUpdateDraw = _fnAjaxUpdateDraw;
6323                this.oApi._fnAddOptionsHtml = _fnAddOptionsHtml;
6324                this.oApi._fnFeatureHtmlTable = _fnFeatureHtmlTable;
6325                this.oApi._fnScrollDraw = _fnScrollDraw;
6326                this.oApi._fnAjustColumnSizing = _fnAjustColumnSizing;
6327                this.oApi._fnFeatureHtmlFilter = _fnFeatureHtmlFilter;
6328                this.oApi._fnFilterComplete = _fnFilterComplete;
6329                this.oApi._fnFilterCustom = _fnFilterCustom;
6330                this.oApi._fnFilterColumn = _fnFilterColumn;
6331                this.oApi._fnFilter = _fnFilter;
6332                this.oApi._fnBuildSearchArray = _fnBuildSearchArray;
6333                this.oApi._fnBuildSearchRow = _fnBuildSearchRow;
6334                this.oApi._fnFilterCreateSearch = _fnFilterCreateSearch;
6335                this.oApi._fnDataToSearch = _fnDataToSearch;
6336                this.oApi._fnSort = _fnSort;
6337                this.oApi._fnSortAttachListener = _fnSortAttachListener;
6338                this.oApi._fnSortingClasses = _fnSortingClasses;
6339                this.oApi._fnFeatureHtmlPaginate = _fnFeatureHtmlPaginate;
6340                this.oApi._fnPageChange = _fnPageChange;
6341                this.oApi._fnFeatureHtmlInfo = _fnFeatureHtmlInfo;
6342                this.oApi._fnUpdateInfo = _fnUpdateInfo;
6343                this.oApi._fnFeatureHtmlLength = _fnFeatureHtmlLength;
6344                this.oApi._fnFeatureHtmlProcessing = _fnFeatureHtmlProcessing;
6345                this.oApi._fnProcessingDisplay = _fnProcessingDisplay;
6346                this.oApi._fnVisibleToColumnIndex = _fnVisibleToColumnIndex;
6347                this.oApi._fnColumnIndexToVisible = _fnColumnIndexToVisible;
6348                this.oApi._fnNodeToDataIndex = _fnNodeToDataIndex;
6349                this.oApi._fnVisbleColumns = _fnVisbleColumns;
6350                this.oApi._fnCalculateEnd = _fnCalculateEnd;
6351                this.oApi._fnConvertToWidth = _fnConvertToWidth;
6352                this.oApi._fnCalculateColumnWidths = _fnCalculateColumnWidths;
6353                this.oApi._fnScrollingWidthAdjust = _fnScrollingWidthAdjust;
6354                this.oApi._fnGetWidestNode = _fnGetWidestNode;
6355                this.oApi._fnGetMaxLenString = _fnGetMaxLenString;
6356                this.oApi._fnStringToCss = _fnStringToCss;
6357                this.oApi._fnArrayCmp = _fnArrayCmp;
6358                this.oApi._fnDetectType = _fnDetectType;
6359                this.oApi._fnSettingsFromNode = _fnSettingsFromNode;
6360                this.oApi._fnGetDataMaster = _fnGetDataMaster;
6361                this.oApi._fnGetTrNodes = _fnGetTrNodes;
6362                this.oApi._fnGetTdNodes = _fnGetTdNodes;
6363                this.oApi._fnEscapeRegex = _fnEscapeRegex;
6364                this.oApi._fnDeleteIndex = _fnDeleteIndex;
6365                this.oApi._fnReOrderIndex = _fnReOrderIndex;
6366                this.oApi._fnColumnOrdering = _fnColumnOrdering;
6367                this.oApi._fnLog = _fnLog;
6368                this.oApi._fnClearTable = _fnClearTable;
6369                this.oApi._fnSaveState = _fnSaveState;
6370                this.oApi._fnLoadState = _fnLoadState;
6371                this.oApi._fnCreateCookie = _fnCreateCookie;
6372                this.oApi._fnReadCookie = _fnReadCookie;
6373                this.oApi._fnGetUniqueThs = _fnGetUniqueThs;
6374                this.oApi._fnScrollBarWidth = _fnScrollBarWidth;
6375                this.oApi._fnApplyToChildren = _fnApplyToChildren;
6376                this.oApi._fnMap = _fnMap;
6377               
6378               
6379                /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6380                 * Section - Constructor
6381                 */
6382               
6383                /* Want to be able to reference "this" inside the this.each function */
6384                var _that = this;
6385                return this.each(function()
6386                {
6387                        var i=0, iLen, j, jLen, k, kLen;
6388                       
6389                        /* Check to see if we are re-initalising a table */
6390                        for ( i=0, iLen=_aoSettings.length ; i<iLen ; i++ )
6391                        {
6392                                /* Base check on table node */
6393                                if ( _aoSettings[i].nTable == this )
6394                                {
6395                                        if ( typeof oInit == 'undefined' ||
6396                                           ( typeof oInit.bRetrieve != 'undefined' && oInit.bRetrieve === true ) )
6397                                        {
6398                                                return _aoSettings[i].oInstance;
6399                                        }
6400                                        else if ( typeof oInit.bDestroy != 'undefined' && oInit.bDestroy === true )
6401                                        {
6402                                                _aoSettings[i].oInstance.fnDestroy();
6403                                                break;
6404                                        }
6405                                        else
6406                                        {
6407                                                _fnLog( _aoSettings[i], 0, "Cannot reinitialise DataTable.\n\n"+
6408                                                        "To retrieve the DataTables object for this table, please pass either no arguments "+
6409                                                        "to the dataTable() function, or set bRetrieve to true. Alternatively, to destory "+
6410                                                        "the old table and create a new one, set bDestroy to true (note that a lot of "+
6411                                                        "changes to the configuration can be made through the API which is usually much "+
6412                                                        "faster)." );
6413                                                return;
6414                                        }
6415                                }
6416                               
6417                                /* If the element we are initialising has the same ID as a table which was previously
6418                                 * initialised, but the table nodes don't match (from before) then we destory the old
6419                                 * instance by simply deleting it. This is under the assumption that the table has been
6420                                 * destroyed by other methods. Anyone using non-id selectors will need to do this manually
6421                                 */
6422                                if ( _aoSettings[i].sTableId !== "" && _aoSettings[i].sTableId == this.getAttribute('id') )
6423                                {
6424                                        _aoSettings.splice( i, 1 );
6425                                        break;
6426                                }
6427                        }
6428                       
6429                        /* Make a complete and independent copy of the settings object */
6430                        var oSettings = new classSettings();
6431                        _aoSettings.push( oSettings );
6432                       
6433                        var bInitHandedOff = false;
6434                        var bUsePassedData = false;
6435                       
6436                        /* Set the id */
6437                        var sId = this.getAttribute( 'id' );
6438                        if ( sId !== null )
6439                        {
6440                                oSettings.sTableId = sId;
6441                                oSettings.sInstance = sId;
6442                        }
6443                        else
6444                        {
6445                                oSettings.sInstance = _oExt._oExternConfig.iNextUnique ++;
6446                        }
6447                       
6448                        /* Sanity check */
6449                        if ( this.nodeName.toLowerCase() != 'table' )
6450                        {
6451                                _fnLog( oSettings, 0, "Attempted to initialise DataTables on a node which is not a "+
6452                                        "table: "+this.nodeName );
6453                                return;
6454                        }
6455                       
6456                        /* Set the table node */
6457                        oSettings.nTable = this;
6458                       
6459                        /* Keep a reference to the 'this' instance for the table. Note that if this table is being
6460                         * created with others, we retrieve a unique instance to ease API access.
6461                         */
6462                        oSettings.oInstance = _that.length == 1 ? _that : $(this).dataTable();
6463                       
6464                        /* Bind the API functions to the settings, so we can perform actions whenever oSettings is
6465                         * available
6466                         */
6467                        oSettings.oApi = _that.oApi;
6468                       
6469                        /* State the table's width for if a destroy is called at a later time */
6470                        oSettings.sDestroyWidth = $(this).width();
6471                       
6472                        /* Store the features that we have available */
6473                        if ( typeof oInit != 'undefined' && oInit !== null )
6474                        {
6475                                oSettings.oInit = oInit;
6476                                _fnMap( oSettings.oFeatures, oInit, "bPaginate" );
6477                                _fnMap( oSettings.oFeatures, oInit, "bLengthChange" );
6478                                _fnMap( oSettings.oFeatures, oInit, "bFilter" );
6479                                _fnMap( oSettings.oFeatures, oInit, "bSort" );
6480                                _fnMap( oSettings.oFeatures, oInit, "bInfo" );
6481                                _fnMap( oSettings.oFeatures, oInit, "bProcessing" );
6482                                _fnMap( oSettings.oFeatures, oInit, "bAutoWidth" );
6483                                _fnMap( oSettings.oFeatures, oInit, "bSortClasses" );
6484                                _fnMap( oSettings.oFeatures, oInit, "bServerSide" );
6485                                _fnMap( oSettings.oScroll, oInit, "sScrollX", "sX" );
6486                                _fnMap( oSettings.oScroll, oInit, "sScrollXInner", "sXInner" );
6487                                _fnMap( oSettings.oScroll, oInit, "sScrollY", "sY" );
6488                                _fnMap( oSettings.oScroll, oInit, "bScrollCollapse", "bCollapse" );
6489                                _fnMap( oSettings.oScroll, oInit, "bScrollInfinite", "bInfinite" );
6490                                _fnMap( oSettings.oScroll, oInit, "iScrollLoadGap", "iLoadGap" );
6491                                _fnMap( oSettings.oScroll, oInit, "bScrollAutoCss", "bAutoCss" );
6492                                _fnMap( oSettings, oInit, "asStripClasses" );
6493                                _fnMap( oSettings, oInit, "fnRowCallback" );
6494                                _fnMap( oSettings, oInit, "fnHeaderCallback" );
6495                                _fnMap( oSettings, oInit, "fnFooterCallback" );
6496                                _fnMap( oSettings, oInit, "fnCookieCallback" );
6497                                _fnMap( oSettings, oInit, "fnInitComplete" );
6498                                _fnMap( oSettings, oInit, "fnServerData" );
6499                                _fnMap( oSettings, oInit, "fnFormatNumber" );
6500                                _fnMap( oSettings, oInit, "aaSorting" );
6501                                _fnMap( oSettings, oInit, "aaSortingFixed" );
6502                                _fnMap( oSettings, oInit, "aLengthMenu" );
6503                                _fnMap( oSettings, oInit, "sPaginationType" );
6504                                _fnMap( oSettings, oInit, "sAjaxSource" );
6505                                _fnMap( oSettings, oInit, "iCookieDuration" );
6506                                _fnMap( oSettings, oInit, "sCookiePrefix" );
6507                                _fnMap( oSettings, oInit, "sDom" );
6508                                _fnMap( oSettings, oInit, "oSearch", "oPreviousSearch" );
6509                                _fnMap( oSettings, oInit, "aoSearchCols", "aoPreSearchCols" );
6510                                _fnMap( oSettings, oInit, "iDisplayLength", "_iDisplayLength" );
6511                                _fnMap( oSettings, oInit, "bJQueryUI", "bJUI" );
6512                                _fnMap( oSettings.oLanguage, oInit, "fnInfoCallback" );
6513                               
6514                                /* Callback functions which are array driven */
6515                                if ( typeof oInit.fnDrawCallback == 'function' )
6516                                {
6517                                        oSettings.aoDrawCallback.push( {
6518                                                "fn": oInit.fnDrawCallback,
6519                                                "sName": "user"
6520                                        } );
6521                                }
6522                               
6523                                if ( typeof oInit.fnStateSaveCallback == 'function' )
6524                                {
6525                                        oSettings.aoStateSave.push( {
6526                                                "fn": oInit.fnStateSaveCallback,
6527                                                "sName": "user"
6528                                        } );
6529                                }
6530                               
6531                                if ( typeof oInit.fnStateLoadCallback == 'function' )
6532                                {
6533                                        oSettings.aoStateLoad.push( {
6534                                                "fn": oInit.fnStateLoadCallback,
6535                                                "sName": "user"
6536                                        } );
6537                                }
6538                               
6539                                if ( oSettings.oFeatures.bServerSide && oSettings.oFeatures.bSort &&
6540                                           oSettings.oFeatures.bSortClasses )
6541                                {
6542                                        /* Enable sort classes for server-side processing. Safe to do it here, since server-side
6543                                         * processing must be enabled by the developer
6544                                         */
6545                                        oSettings.aoDrawCallback.push( {
6546                                                "fn": _fnSortingClasses,
6547                                                "sName": "server_side_sort_classes"
6548                                        } );
6549                                }
6550                               
6551                                if ( typeof oInit.bJQueryUI != 'undefined' && oInit.bJQueryUI )
6552                                {
6553                                        /* Use the JUI classes object for display. You could clone the oStdClasses object if
6554                                         * you want to have multiple tables with multiple independent classes
6555                                         */
6556                                        oSettings.oClasses = _oExt.oJUIClasses;
6557                                       
6558                                        if ( typeof oInit.sDom == 'undefined' )
6559                                        {
6560                                                /* Set the DOM to use a layout suitable for jQuery UI's theming */
6561                                                oSettings.sDom = '<"H"lfr>t<"F"ip>';
6562                                        }
6563                                }
6564                               
6565                                /* Calculate the scroll bar width and cache it for use later on */
6566                                if ( oSettings.oScroll.sX !== "" || oSettings.oScroll.sY !== "" )
6567                                {
6568                                        oSettings.oScroll.iBarWidth = _fnScrollBarWidth();
6569                                }
6570                               
6571                                if ( typeof oInit.iDisplayStart != 'undefined' &&
6572                                     typeof oSettings.iInitDisplayStart == 'undefined' )
6573                                {
6574                                        /* Display start point, taking into account the save saving */
6575                                        oSettings.iInitDisplayStart = oInit.iDisplayStart;
6576                                        oSettings._iDisplayStart = oInit.iDisplayStart;
6577                                }
6578                               
6579                                /* Must be done after everything which can be overridden by a cookie! */
6580                                if ( typeof oInit.bStateSave != 'undefined' )
6581                                {
6582                                        oSettings.oFeatures.bStateSave = oInit.bStateSave;
6583                                        _fnLoadState( oSettings, oInit );
6584                                        oSettings.aoDrawCallback.push( {
6585                                                "fn": _fnSaveState,
6586                                                "sName": "state_save"
6587                                        } );
6588                                }
6589                               
6590                                if ( typeof oInit.aaData != 'undefined' )
6591                                {
6592                                        bUsePassedData = true;
6593                                }
6594                               
6595                                /* Backwards compatability */
6596                                /* aoColumns / aoData - remove at some point... */
6597                                if ( typeof oInit != 'undefined' && typeof oInit.aoData != 'undefined' )
6598                                {
6599                                        oInit.aoColumns = oInit.aoData;
6600                                }
6601                               
6602                                /* Language definitions */
6603                                if ( typeof oInit.oLanguage != 'undefined' )
6604                                {
6605                                        if ( typeof oInit.oLanguage.sUrl != 'undefined' && oInit.oLanguage.sUrl !== "" )
6606                                        {
6607                                                /* Get the language definitions from a file */
6608                                                oSettings.oLanguage.sUrl = oInit.oLanguage.sUrl;
6609                                                $.getJSON( oSettings.oLanguage.sUrl, null, function( json ) {
6610                                                        _fnLanguageProcess( oSettings, json, true ); } );
6611                                                bInitHandedOff = true;
6612                                        }
6613                                        else
6614                                        {
6615                                                _fnLanguageProcess( oSettings, oInit.oLanguage, false );
6616                                        }
6617                                }
6618                                /* Warning: The _fnLanguageProcess function is async to the remainder of this function due
6619                                 * to the XHR. We use _bInitialised in _fnLanguageProcess() to check this the processing
6620                                 * below is complete. The reason for spliting it like this is optimisation - we can fire
6621                                 * off the XHR (if needed) and then continue processing the data.
6622                                 */
6623                        }
6624                        else
6625                        {
6626                                /* Create a dummy object for quick manipulation later on. */
6627                                oInit = {};
6628                        }
6629                       
6630                        /*
6631                         * Stripes
6632                         * Add the strip classes now that we know which classes to apply - unless overruled
6633                         */
6634                        if ( typeof oInit.asStripClasses == 'undefined' )
6635                        {
6636                                oSettings.asStripClasses.push( oSettings.oClasses.sStripOdd );
6637                                oSettings.asStripClasses.push( oSettings.oClasses.sStripEven );
6638                        }
6639                       
6640                        /* Remove row stripe classes if they are already on the table row */
6641                        var bStripeRemove = false;
6642                        var anRows = $('>tbody>tr', this);
6643                        for ( i=0, iLen=oSettings.asStripClasses.length ; i<iLen ; i++ )
6644                        {
6645                                if ( anRows.filter(":lt(2)").hasClass( oSettings.asStripClasses[i]) )
6646                                {
6647                                        bStripeRemove = true;
6648                                        break;
6649                                }
6650                        }
6651                                       
6652                        if ( bStripeRemove )
6653                        {
6654                                /* Store the classes which we are about to remove so they can be readded on destory */
6655                                oSettings.asDestoryStrips = [ '', '' ];
6656                                if ( $(anRows[0]).hasClass(oSettings.oClasses.sStripOdd) )
6657                                {
6658                                        oSettings.asDestoryStrips[0] += oSettings.oClasses.sStripOdd+" ";
6659                                }
6660                                if ( $(anRows[0]).hasClass(oSettings.oClasses.sStripEven) )
6661                                {
6662                                        oSettings.asDestoryStrips[0] += oSettings.oClasses.sStripEven;
6663                                }
6664                                if ( $(anRows[1]).hasClass(oSettings.oClasses.sStripOdd) )
6665                                {
6666                                        oSettings.asDestoryStrips[1] += oSettings.oClasses.sStripOdd+" ";
6667                                }
6668                                if ( $(anRows[1]).hasClass(oSettings.oClasses.sStripEven) )
6669                                {
6670                                        oSettings.asDestoryStrips[1] += oSettings.oClasses.sStripEven;
6671                                }
6672                               
6673                                anRows.removeClass( oSettings.asStripClasses.join(' ') );
6674                        }
6675                       
6676                        /*
6677                         * Columns
6678                         * See if we should load columns automatically or use defined ones
6679                         */
6680                        var nThead = this.getElementsByTagName('thead');
6681                        var anThs = nThead.length===0 ? [] : _fnGetUniqueThs( nThead[0] );
6682                        var aoColumnsInit;
6683                       
6684                        /* If not given a column array, generate one with nulls */
6685                        if ( typeof oInit.aoColumns == 'undefined' )
6686                        {
6687                                aoColumnsInit = [];
6688                                for ( i=0, iLen=anThs.length ; i<iLen ; i++ )
6689                                {
6690                                        aoColumnsInit.push( null );
6691                                }
6692                        }
6693                        else
6694                        {
6695                                aoColumnsInit = oInit.aoColumns;
6696                        }
6697                       
6698                        /* Add the columns */
6699                        for ( i=0, iLen=aoColumnsInit.length ; i<iLen ; i++ )
6700                        {
6701                                /* Check if we have column visibilty state to restore */
6702                                if ( typeof oInit.saved_aoColumns != 'undefined' && oInit.saved_aoColumns.length == iLen )
6703                                {
6704                                        if ( aoColumnsInit[i] === null )
6705                                        {
6706                                                aoColumnsInit[i] = {};
6707                                        }
6708                                        aoColumnsInit[i].bVisible = oInit.saved_aoColumns[i].bVisible;
6709                                }
6710                               
6711                                _fnAddColumn( oSettings, anThs ? anThs[i] : null );
6712                        }
6713                       
6714                        /* Add options from column definations */
6715                        if ( typeof oInit.aoColumnDefs != 'undefined' )
6716                        {
6717                                /* Loop over the column defs array - loop in reverse so first instace has priority */
6718                                for ( i=oInit.aoColumnDefs.length-1 ; i>=0 ; i-- )
6719                                {
6720                                        /* Each column def can target multiple columns, as it is an array */
6721                                        var aTargets = oInit.aoColumnDefs[i].aTargets;
6722                                        if ( !$.isArray( aTargets ) )
6723                                        {
6724                                                _fnLog( oSettings, 1, 'aTargets must be an array of targets, not a '+(typeof aTargets) );
6725                                        }
6726                                        for ( j=0, jLen=aTargets.length ; j<jLen ; j++ )
6727                                        {
6728                                                if ( typeof aTargets[j] == 'number' && aTargets[j] >= 0 )
6729                                                {
6730                                                        /* 0+ integer, left to right column counting. We add columns which are unknown
6731                                                         * automatically. Is this the right behaviour for this? We should at least
6732                                                         * log it in future. We cannot do this for the negative or class targets, only here.
6733                                                         */
6734                                                        while( oSettings.aoColumns.length <= aTargets[j] )
6735                                                        {
6736                                                                _fnAddColumn( oSettings );
6737                                                        }
6738                                                        _fnColumnOptions( oSettings, aTargets[j], oInit.aoColumnDefs[i] );
6739                                                }
6740                                                else if ( typeof aTargets[j] == 'number' && aTargets[j] < 0 )
6741                                                {
6742                                                        /* Negative integer, right to left column counting */
6743                                                        _fnColumnOptions( oSettings, oSettings.aoColumns.length+aTargets[j],
6744                                                                oInit.aoColumnDefs[i] );
6745                                                }
6746                                                else if ( typeof aTargets[j] == 'string' )
6747                                                {
6748                                                        /* Class name matching on TH element */
6749                                                        for ( k=0, kLen=oSettings.aoColumns.length ; k<kLen ; k++ )
6750                                                        {
6751                                                                if ( aTargets[j] == "_all" ||
6752                                                                     oSettings.aoColumns[k].nTh.className.indexOf( aTargets[j] ) != -1 )
6753                                                                {
6754                                                                        _fnColumnOptions( oSettings, k, oInit.aoColumnDefs[i] );
6755                                                                }
6756                                                        }
6757                                                }
6758                                        }
6759                                }
6760                        }
6761                       
6762                        /* Add options from column array - after the defs array so this has priority */
6763                        if ( typeof aoColumnsInit != 'undefined' )
6764                        {
6765                                for ( i=0, iLen=aoColumnsInit.length ; i<iLen ; i++ )
6766                                {
6767                                        _fnColumnOptions( oSettings, i, aoColumnsInit[i] );
6768                                }
6769                        }
6770                       
6771                        /*
6772                         * Sorting
6773                         * Check the aaSorting array
6774                         */
6775                        for ( i=0, iLen=oSettings.aaSorting.length ; i<iLen ; i++ )
6776                        {
6777                                if ( oSettings.aaSorting[i][0] >= oSettings.aoColumns.length )
6778                                {
6779                                        oSettings.aaSorting[i][0] = 0;
6780                                }
6781                                var oColumn = oSettings.aoColumns[ oSettings.aaSorting[i][0] ];
6782                               
6783                                /* Add a default sorting index */
6784                                if ( typeof oSettings.aaSorting[i][2] == 'undefined' )
6785                                {
6786                                        oSettings.aaSorting[i][2] = 0;
6787                                }
6788                               
6789                                /* If aaSorting is not defined, then we use the first indicator in asSorting */
6790                                if ( typeof oInit.aaSorting == "undefined" &&
6791                                                 typeof oSettings.saved_aaSorting == "undefined" )
6792                                {
6793                                        oSettings.aaSorting[i][1] = oColumn.asSorting[0];
6794                                }
6795                               
6796                                /* Set the current sorting index based on aoColumns.asSorting */
6797                                for ( j=0, jLen=oColumn.asSorting.length ; j<jLen ; j++ )
6798                                {
6799                                        if ( oSettings.aaSorting[i][1] == oColumn.asSorting[j] )
6800                                        {
6801                                                oSettings.aaSorting[i][2] = j;
6802                                                break;
6803                                        }
6804                                }
6805                        }
6806                               
6807                        /* Do a first pass on the sorting classes (allows any size changes to be taken into
6808                         * account, and also will apply sorting disabled classes if disabled
6809                         */
6810                        _fnSortingClasses( oSettings );
6811                       
6812                        /*
6813                         * Final init
6814                         * Sanity check that there is a thead and tbody. If not let's just create them
6815                         */
6816                        if ( this.getElementsByTagName('thead').length === 0 )
6817                        {
6818                                this.appendChild( document.createElement( 'thead' ) );
6819                        }
6820                       
6821                        if ( this.getElementsByTagName('tbody').length === 0 )
6822                        {
6823                                this.appendChild( document.createElement( 'tbody' ) );
6824                        }
6825                       
6826                        oSettings.nTHead = this.getElementsByTagName('thead')[0];
6827                        oSettings.nTBody = this.getElementsByTagName('tbody')[0];
6828                        if ( this.getElementsByTagName('tfoot').length > 0 )
6829                        {
6830                                oSettings.nTFoot = this.getElementsByTagName('tfoot')[0];
6831                        }
6832                       
6833                        /* Check if there is data passing into the constructor */
6834                        if ( bUsePassedData )
6835                        {
6836                                for ( i=0 ; i<oInit.aaData.length ; i++ )
6837                                {
6838                                        _fnAddData( oSettings, oInit.aaData[ i ] );
6839                                }
6840                        }
6841                        else
6842                        {
6843                                /* Grab the data from the page */
6844                                _fnGatherData( oSettings );
6845                        }
6846                       
6847                        /* Copy the data index array */
6848                        oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
6849                       
6850                        /* Initialisation complete - table can be drawn */
6851                        oSettings.bInitialised = true;
6852                       
6853                        /* Check if we need to initialise the table (it might not have been handed off to the
6854                         * language processor)
6855                         */
6856                        if ( bInitHandedOff === false )
6857                        {
6858                                _fnInitalise( oSettings );
6859                        }
6860                });
6861        };
6862})(jQuery, window, document);
Note: See TracBrowser for help on using the repository browser.