Ignore:
Timestamp:
Feb 5, 2011, 9:18:21 PM (13 years ago)
Author:
Eric
Message:

bug 2072 and bug 2140 merged from trunk to branch 2.20

File:
1 edited

Legend:

Unmodified
Added
Removed
  • extensions/NBC_UserAdvManager/branches/2.20/admin/template/js/jquery.tablesorter.js

    r6378 r9091  
    22 *
    33 * TableSorter 2.0 - Client-side table sorting with ease!
    4  * Version 2.0.3
     4 * Version 2.0.5b
    55 * @requires jQuery v1.2.3
    66 *
     
    1313 */
    1414/**
    15  *
     15 * 
    1616 * @description Create a sortable table with multi-column sorting capabilitys
    1717 *
    1818 * @example $('table').tablesorter();
    1919 * @desc Create a simple tablesorter interface.
    20  *
     20 * 
    2121 * @example $('table').tablesorter({ sortList:[[0,0],[1,0]] });
    22  * @desc Create a tablesorter interface and sort on the first and secound column in ascending order.
     22 * @desc Create a tablesorter interface and sort on the first and secound column column headers.
    2323 *
    2424 * @example $('table').tablesorter({ headers: { 0: { sorter: false}, 1: {sorter: false} } });
    25  * @desc Create a tablesorter interface and disableing the first and secound column headers.
    26  *
    27  * @example $('table').tablesorter({ 0: {sorter:"integer"}, 1: {sorter:"currency"} });
    28  * @desc Create a tablesorter interface and set a column parser for the first and secound column.
    29  *
    30  *
    31  * @param Object settings An object literal containing key/value pairs to provide optional settings.
    32  *
    33  * @option String cssHeader (optional)                  A string of the class name to be appended to sortable tr elements in the thead of the table.
    34  *                                                                                              Default value: "header"
    35  *
    36  * @option String cssAsc (optional)                     A string of the class name to be appended to sortable tr elements in the thead on a ascending sort.
    37  *                                                                                              Default value: "headerSortUp"
    38  *
    39  * @option String cssDesc (optional)                    A string of the class name to be appended to sortable tr elements in the thead on a descending sort.
    40  *                                                                                              Default value: "headerSortDown"
    41  *
    42  * @option String sortInitialOrder (optional)   A string of the inital sorting order can be asc or desc.
    43  *                                                                                              Default value: "asc"
    44  *
    45  * @option String sortMultisortKey (optional)   A string of the multi-column sort key.
    46  *                                                                                              Default value: "shiftKey"
    47  *
    48  * @option String textExtraction (optional)     A string of the text-extraction method to use.
    49  *                                                                                              For complex html structures inside td cell set this option to "complex",
    50  *                                                                                              on large tables the complex option can be slow.
    51  *                                                                                              Default value: "simple"
    52  *
    53  * @option Object headers (optional)                    An array containing the forces sorting rules.
    54  *                                                                                              This option let's you specify a default sorting rule.
    55  *                                                                                              Default value: null
    56  *
    57  * @option Array sortList (optional)                    An array containing the forces sorting rules.
    58  *                                                                                              This option let's you specify a default sorting rule.
    59  *                                                                                              Default value: null
    60  *
    61  * @option Array sortForce (optional)                   An array containing forced sorting rules.
    62  *                                                                                              This option let's you specify a default sorting rule, which is prepended to user-selected rules.
    63  *                                                                                              Default value: null
    64  * 
    65   * @option Array sortAppend (optional)                         An array containing forced sorting rules.
    66  *                                                                                              This option let's you specify a default sorting rule, which is appended to user-selected rules.
    67  *                                                                                              Default value: null
    68  *
    69  * @option Boolean widthFixed (optional)                Boolean flag indicating if tablesorter should apply fixed widths to the table columns.
    70  *                                                                                              This is usefull when using the pager companion plugin.
    71  *                                                                                              This options requires the dimension jquery plugin.
    72  *                                                                                              Default value: false
    73  *
    74  * @option Boolean cancelSelection (optional)   Boolean flag indicating if tablesorter should cancel selection of the table headers text.
    75  *                                                                                              Default value: true
    76  *
    77  * @option Boolean debug (optional)                     Boolean flag indicating if tablesorter should display debuging information usefull for development.
    78  *
     25 *         
     26 * @desc Create a tablesorter interface and disableing the first and second  column headers.
     27 *     
     28 *
     29 * @example $('table').tablesorter({ headers: { 0: {sorter:"integer"}, 1: {sorter:"currency"} } });
     30 *
     31 * @desc Create a tablesorter interface and set a column parser for the first
     32 *       and second column.
     33 *
     34 *
     35 * @param Object
     36 *            settings An object literal containing key/value pairs to provide
     37 *            optional settings.
     38 *
     39 *
     40 * @option String cssHeader (optional) A string of the class name to be appended
     41 *         to sortable tr elements in the thead of the table. Default value:
     42 *         "header"
     43 *
     44 * @option String cssAsc (optional) A string of the class name to be appended to
     45 *         sortable tr elements in the thead on a ascending sort. Default value:
     46 *         "headerSortUp"
     47 *
     48 * @option String cssDesc (optional) A string of the class name to be appended
     49 *         to sortable tr elements in the thead on a descending sort. Default
     50 *         value: "headerSortDown"
     51 *
     52 * @option String sortInitialOrder (optional) A string of the inital sorting
     53 *         order can be asc or desc. Default value: "asc"
     54 *
     55 * @option String sortMultisortKey (optional) A string of the multi-column sort
     56 *         key. Default value: "shiftKey"
     57 *
     58 * @option String textExtraction (optional) A string of the text-extraction
     59 *         method to use. For complex html structures inside td cell set this
     60 *         option to "complex", on large tables the complex option can be slow.
     61 *         Default value: "simple"
     62 *
     63 * @option Object headers (optional) An array containing the forces sorting
     64 *         rules. This option let's you specify a default sorting rule. Default
     65 *         value: null
     66 *
     67 * @option Array sortList (optional) An array containing the forces sorting
     68 *         rules. This option let's you specify a default sorting rule. Default
     69 *         value: null
     70 *
     71 * @option Array sortForce (optional) An array containing forced sorting rules.
     72 *         This option let's you specify a default sorting rule, which is
     73 *         prepended to user-selected rules. Default value: null
     74 *
     75 * @option Boolean sortLocaleCompare (optional) Boolean flag indicating whatever
     76 *         to use String.localeCampare method or not. Default set to true.
     77 *
     78 *
     79 * @option Array sortAppend (optional) An array containing forced sorting rules.
     80 *         This option let's you specify a default sorting rule, which is
     81 *         appended to user-selected rules. Default value: null
     82 *
     83 * @option Boolean widthFixed (optional) Boolean flag indicating if tablesorter
     84 *         should apply fixed widths to the table columns. This is usefull when
     85 *         using the pager companion plugin. This options requires the dimension
     86 *         jquery plugin. Default value: false
     87 *
     88 * @option Boolean cancelSelection (optional) Boolean flag indicating if
     89 *         tablesorter should cancel selection of the table headers text.
     90 *         Default value: true
     91 *
     92 * @option Boolean debug (optional) Boolean flag indicating if tablesorter
     93 *         should display debuging information usefull for development.
     94 *
    7995 * @type jQuery
    80  *
     96 * 
    8197 * @name tablesorter
    8298 *
     
    86102 */
    87103
    88 (function($) {
    89         $.extend({
    90                 tablesorter: new function() {
     104(function ($) {
     105    $.extend({
     106        tablesorter: new
     107        function () {
     108
     109            var parsers = [],
     110                widgets = [];
     111
     112            this.defaults = {
     113                cssHeader: "header",
     114                cssAsc: "headerSortUp",
     115                cssDesc: "headerSortDown",
     116                cssChildRow: "expand-child",
     117                sortInitialOrder: "asc",
     118                sortMultiSortKey: "shiftKey",
     119                sortForce: null,
     120                sortAppend: null,
     121                sortLocaleCompare: true,
     122                textExtraction: "simple",
     123                parsers: {}, widgets: [],
     124                widgetZebra: {
     125                    css: ["even", "odd"]
     126                }, headers: {}, widthFixed: false,
     127                cancelSelection: true,
     128                sortList: [],
     129                headerList: [],
     130                dateFormat: "us",
     131                decimal: '/\.|\,/g',
     132                onRenderHeader: null,
     133                selectorHeaders: 'thead th',
     134                debug: false
     135            };
     136
     137            /* debuging utils */
     138
     139            function benchmark(s, d) {
     140                log(s + "," + (new Date().getTime() - d.getTime()) + "ms");
     141            }
     142
     143            this.benchmark = benchmark;
     144
     145            function log(s) {
     146                if (typeof console != "undefined" && typeof console.debug != "undefined") {
     147                    console.log(s);
     148                } else {
     149                    alert(s);
     150                }
     151            }
     152
     153            /* parsers utils */
     154
     155            function buildParserCache(table, $headers) {
     156
     157                if (table.config.debug) {
     158                    var parsersDebug = "";
     159                }
     160
     161                if (table.tBodies.length == 0) return; // In the case of empty tables
     162                var rows = table.tBodies[0].rows;
     163
     164                if (rows[0]) {
     165
     166                    var list = [],
     167                        cells = rows[0].cells,
     168                        l = cells.length;
     169
     170                    for (var i = 0; i < l; i++) {
     171
     172                        var p = false;
     173
     174                        if ($.metadata && ($($headers[i]).metadata() && $($headers[i]).metadata().sorter)) {
     175
     176                            p = getParserById($($headers[i]).metadata().sorter);
     177
     178                        } else if ((table.config.headers[i] && table.config.headers[i].sorter)) {
     179
     180                            p = getParserById(table.config.headers[i].sorter);
     181                        }
     182                        if (!p) {
     183
     184                            p = detectParserForColumn(table, rows, -1, i);
     185                        }
     186
     187                        if (table.config.debug) {
     188                            parsersDebug += "column:" + i + " parser:" + p.id + "\n";
     189                        }
     190
     191                        list.push(p);
     192                    }
     193                }
     194
     195                if (table.config.debug) {
     196                    log(parsersDebug);
     197                }
     198
     199                return list;
     200            };
     201
     202            function detectParserForColumn(table, rows, rowIndex, cellIndex) {
     203                var l = parsers.length,
     204                    node = false,
     205                    nodeValue = false,
     206                    keepLooking = true;
     207                while (nodeValue == '' && keepLooking) {
     208                    rowIndex++;
     209                    if (rows[rowIndex]) {
     210                        node = getNodeFromRowAndCellIndex(rows, rowIndex, cellIndex);
     211                        nodeValue = trimAndGetNodeText(table.config, node);
     212                        if (table.config.debug) {
     213                            log('Checking if value was empty on row:' + rowIndex);
     214                        }
     215                    } else {
     216                        keepLooking = false;
     217                    }
     218                }
     219                for (var i = 1; i < l; i++) {
     220                    if (parsers[i].is(nodeValue, table, node)) {
     221                        return parsers[i];
     222                    }
     223                }
     224                // 0 is always the generic parser (text)
     225                return parsers[0];
     226            }
     227
     228            function getNodeFromRowAndCellIndex(rows, rowIndex, cellIndex) {
     229                return rows[rowIndex].cells[cellIndex];
     230            }
     231
     232            function trimAndGetNodeText(config, node) {
     233                return $.trim(getElementText(config, node));
     234            }
     235
     236            function getParserById(name) {
     237                var l = parsers.length;
     238                for (var i = 0; i < l; i++) {
     239                    if (parsers[i].id.toLowerCase() == name.toLowerCase()) {
     240                        return parsers[i];
     241                    }
     242                }
     243                return false;
     244            }
     245
     246            /* utils */
     247
     248            function buildCache(table) {
     249
     250                if (table.config.debug) {
     251                    var cacheTime = new Date();
     252                }
     253
     254                var totalRows = (table.tBodies[0] && table.tBodies[0].rows.length) || 0,
     255                    totalCells = (table.tBodies[0].rows[0] && table.tBodies[0].rows[0].cells.length) || 0,
     256                    parsers = table.config.parsers,
     257                    cache = {
     258                        row: [],
     259                        normalized: []
     260                    };
     261
     262                for (var i = 0; i < totalRows; ++i) {
     263
     264                    /** Add the table data to main data array */
     265                    var c = $(table.tBodies[0].rows[i]),
     266                        cols = [];
     267
     268                    // if this is a child row, add it to the last row's children and
     269                    // continue to the next row
     270                    if (c.hasClass(table.config.cssChildRow)) {
     271                        cache.row[cache.row.length - 1] = cache.row[cache.row.length - 1].add(c);
     272                        // go to the next for loop
     273                        continue;
     274                    }
     275
     276                    cache.row.push(c);
     277
     278                    for (var j = 0; j < totalCells; ++j) {
     279                        cols.push(parsers[j].format(getElementText(table.config, c[0].cells[j]), table, c[0].cells[j]));
     280                    }
     281
     282                    cols.push(cache.normalized.length); // add position for rowCache
     283                    cache.normalized.push(cols);
     284                    cols = null;
     285                };
     286
     287                if (table.config.debug) {
     288                    benchmark("Building cache for " + totalRows + " rows:", cacheTime);
     289                }
     290
     291                return cache;
     292            };
     293
     294            function getElementText(config, node) {
     295
     296                var text = "";
     297
     298                if (!node) return "";
     299
     300                if (!config.supportsTextContent) config.supportsTextContent = node.textContent || false;
     301
     302                if (config.textExtraction == "simple") {
     303                    if (config.supportsTextContent) {
     304                        text = node.textContent;
     305                    } else {
     306                        if (node.childNodes[0] && node.childNodes[0].hasChildNodes()) {
     307                            text = node.childNodes[0].innerHTML;
     308                        } else {
     309                            text = node.innerHTML;
     310                        }
     311                    }
     312                } else {
     313                    if (typeof(config.textExtraction) == "function") {
     314                        text = config.textExtraction(node);
     315                    } else {
     316                        text = $(node).text();
     317                    }
     318                }
     319                return text;
     320            }
     321
     322            function appendToTable(table, cache) {
     323
     324                if (table.config.debug) {
     325                    var appendTime = new Date()
     326                }
     327
     328                var c = cache,
     329                    r = c.row,
     330                    n = c.normalized,
     331                    totalRows = n.length,
     332                    checkCell = (n[0].length - 1),
     333                    tableBody = $(table.tBodies[0]),
     334                    rows = [];
     335
     336
     337                for (var i = 0; i < totalRows; i++) {
     338                    var pos = n[i][checkCell];
     339
     340                    rows.push(r[pos]);
     341
     342                    if (!table.config.appender) {
     343
     344                        //var o = ;
     345                        var l = r[pos].length;
     346                        for (var j = 0; j < l; j++) {
     347                            tableBody[0].appendChild(r[pos][j]);
     348                        }
     349
     350                        //
     351                    }
     352                }
     353
     354
     355
     356                if (table.config.appender) {
     357
     358                    table.config.appender(table, rows);
     359                }
     360
     361                rows = null;
     362
     363                if (table.config.debug) {
     364                    benchmark("Rebuilt table:", appendTime);
     365                }
     366
     367                // apply table widgets
     368                applyWidget(table);
     369
     370                // trigger sortend
     371                setTimeout(function () {
     372                    $(table).trigger("sortEnd");
     373                }, 0);
     374
     375            };
     376
     377            function buildHeaders(table) {
     378
     379                if (table.config.debug) {
     380                    var time = new Date();
     381                }
     382
     383                var meta = ($.metadata) ? true : false;
     384               
     385                var header_index = computeTableHeaderCellIndexes(table);
     386
     387                $tableHeaders = $(table.config.selectorHeaders, table).each(function (index) {
     388
     389                    this.column = header_index[this.parentNode.rowIndex + "-" + this.cellIndex];
     390                    // this.column = index;
     391                    this.order = formatSortingOrder(table.config.sortInitialOrder);
     392                   
     393                                       
     394                                        this.count = this.order;
     395
     396                    if (checkHeaderMetadata(this) || checkHeaderOptions(table, index)) this.sortDisabled = true;
     397                                        if (checkHeaderOptionsSortingLocked(table, index)) this.order = this.lockedOrder = checkHeaderOptionsSortingLocked(table, index);
     398
     399                    if (!this.sortDisabled) {
     400                        var $th = $(this).addClass(table.config.cssHeader);
     401                        if (table.config.onRenderHeader) table.config.onRenderHeader.apply($th);
     402                    }
     403
     404                    // add cell to headerList
     405                    table.config.headerList[index] = this;
     406                });
     407
     408                if (table.config.debug) {
     409                    benchmark("Built headers:", time);
     410                    log($tableHeaders);
     411                }
     412
     413                return $tableHeaders;
     414
     415            };
     416
     417            // from:
     418            // http://www.javascripttoolbox.com/lib/table/examples.php
     419            // http://www.javascripttoolbox.com/temp/table_cellindex.html
     420
     421
     422            function computeTableHeaderCellIndexes(t) {
     423                var matrix = [];
     424                var lookup = {};
     425                var thead = t.getElementsByTagName('THEAD')[0];
     426                var trs = thead.getElementsByTagName('TR');
     427
     428                for (var i = 0; i < trs.length; i++) {
     429                    var cells = trs[i].cells;
     430                    for (var j = 0; j < cells.length; j++) {
     431                        var c = cells[j];
     432
     433                        var rowIndex = c.parentNode.rowIndex;
     434                        var cellId = rowIndex + "-" + c.cellIndex;
     435                        var rowSpan = c.rowSpan || 1;
     436                        var colSpan = c.colSpan || 1
     437                        var firstAvailCol;
     438                        if (typeof(matrix[rowIndex]) == "undefined") {
     439                            matrix[rowIndex] = [];
     440                        }
     441                        // Find first available column in the first row
     442                        for (var k = 0; k < matrix[rowIndex].length + 1; k++) {
     443                            if (typeof(matrix[rowIndex][k]) == "undefined") {
     444                                firstAvailCol = k;
     445                                break;
     446                            }
     447                        }
     448                        lookup[cellId] = firstAvailCol;
     449                        for (var k = rowIndex; k < rowIndex + rowSpan; k++) {
     450                            if (typeof(matrix[k]) == "undefined") {
     451                                matrix[k] = [];
     452                            }
     453                            var matrixrow = matrix[k];
     454                            for (var l = firstAvailCol; l < firstAvailCol + colSpan; l++) {
     455                                matrixrow[l] = "x";
     456                            }
     457                        }
     458                    }
     459                }
     460                return lookup;
     461            }
     462
     463            function checkCellColSpan(table, rows, row) {
     464                var arr = [],
     465                    r = table.tHead.rows,
     466                    c = r[row].cells;
     467
     468                for (var i = 0; i < c.length; i++) {
     469                    var cell = c[i];
     470
     471                    if (cell.colSpan > 1) {
     472                        arr = arr.concat(checkCellColSpan(table, headerArr, row++));
     473                    } else {
     474                        if (table.tHead.length == 1 || (cell.rowSpan > 1 || !r[row + 1])) {
     475                            arr.push(cell);
     476                        }
     477                        // headerArr[row] = (i+row);
     478                    }
     479                }
     480                return arr;
     481            };
     482
     483            function checkHeaderMetadata(cell) {
     484                if (($.metadata) && ($(cell).metadata().sorter === false)) {
     485                    return true;
     486                };
     487                return false;
     488            }
     489
     490            function checkHeaderOptions(table, i) {
     491                if ((table.config.headers[i]) && (table.config.headers[i].sorter === false)) {
     492                    return true;
     493                };
     494                return false;
     495            }
    91496                       
    92                         var parsers = [], widgets = [];
     497                         function checkHeaderOptionsSortingLocked(table, i) {
     498                if ((table.config.headers[i]) && (table.config.headers[i].lockedOrder)) return table.config.headers[i].lockedOrder;
     499                return false;
     500            }
    93501                       
    94                         this.defaults = {
    95                                 cssHeader: "header",
    96                                 cssAsc: "headerSortUp",
    97                                 cssDesc: "headerSortDown",
    98                                 sortInitialOrder: "asc",
    99                                 sortMultiSortKey: "shiftKey",
    100                                 sortForce: null,
    101                                 sortAppend: null,
    102                                 textExtraction: "simple",
    103                                 parsers: {},
    104                                 widgets: [],           
    105                                 widgetZebra: {css: ["even","odd"]},
    106                                 headers: {},
    107                                 widthFixed: false,
    108                                 cancelSelection: true,
    109                                 sortList: [],
    110                                 headerList: [],
    111                                 dateFormat: "us",
    112                                 decimal: '.',
    113                                 debug: false
    114                         };
    115                        
    116                         /* debuging utils */
    117                         function benchmark(s,d) {
    118                                 log(s + "," + (new Date().getTime() - d.getTime()) + "ms");
    119                         }
    120                        
    121                         this.benchmark = benchmark;
    122                        
    123                         function log(s) {
    124                                 if (typeof console != "undefined" && typeof console.debug != "undefined") {
    125                                         console.log(s);
    126                                 } else {
    127                                         alert(s);
    128                                 }
    129                         }
    130                                                
    131                         /* parsers utils */
    132                         function buildParserCache(table,$headers) {
    133                                
    134                                 if(table.config.debug) { var parsersDebug = ""; }
    135                                
    136                                 var rows = table.tBodies[0].rows;
    137                                
    138                                 if(table.tBodies[0].rows[0]) {
    139 
    140                                         var list = [], cells = rows[0].cells, l = cells.length;
    141                                        
    142                                         for (var i=0;i < l; i++) {
    143                                                 var p = false;
    144                                                
    145                                                 if($.metadata && ($($headers[i]).metadata() && $($headers[i]).metadata().sorter)  ) {
    146                                                
    147                                                         p = getParserById($($headers[i]).metadata().sorter);   
    148                                                
    149                                                 } else if((table.config.headers[i] && table.config.headers[i].sorter)) {
    150        
    151                                                         p = getParserById(table.config.headers[i].sorter);
    152                                                 }
    153                                                 if(!p) {
    154                                                         p = detectParserForColumn(table,cells[i]);
    155                                                 }
    156        
    157                                                 if(table.config.debug) { parsersDebug += "column:" + i + " parser:" +p.id + "\n"; }
    158        
    159                                                 list.push(p);
    160                                         }
    161                                 }
    162                                
    163                                 if(table.config.debug) { log(parsersDebug); }
    164 
    165                                 return list;
    166                         };
    167                        
    168                         function detectParserForColumn(table,node) {
    169                                 var l = parsers.length;
    170                                 for(var i=1; i < l; i++) {
    171                                         if(parsers[i].is($.trim(getElementText(table.config,node)),table,node)) {
    172                                                 return parsers[i];
    173                                         }
    174                                 }
    175                                 // 0 is always the generic parser (text)
    176                                 return parsers[0];
    177                         }
    178                        
    179                         function getParserById(name) {
    180                                 var l = parsers.length;
    181                                 for(var i=0; i < l; i++) {
    182                                         if(parsers[i].id.toLowerCase() == name.toLowerCase()) {
    183                                                 return parsers[i];
    184                                         }
    185                                 }
    186                                 return false;
    187                         }
    188                        
    189                         /* utils */
    190                         function buildCache(table) {
    191                                
    192                                 if(table.config.debug) { var cacheTime = new Date(); }
    193                                
    194                                
    195                                 var totalRows = (table.tBodies[0] && table.tBodies[0].rows.length) || 0,
    196                                         totalCells = (table.tBodies[0].rows[0] && table.tBodies[0].rows[0].cells.length) || 0,
    197                                         parsers = table.config.parsers,
    198                                         cache = {row: [], normalized: []};
    199                                
    200                                         for (var i=0;i < totalRows; ++i) {
    201                                        
    202                                                 /** Add the table data to main data array */
    203                                                 var c = table.tBodies[0].rows[i], cols = [];
    204                                        
    205                                                 cache.row.push($(c));
    206                                                
    207                                                 for(var j=0; j < totalCells; ++j) {
    208                                                         cols.push(parsers[j].format(getElementText(table.config,c.cells[j]),table,c.cells[j]));
    209                                                 }
    210                                                                                                
    211                                                 cols.push(i); // add position for rowCache
    212                                                 cache.normalized.push(cols);
    213                                                 cols = null;
    214                                         };
    215                                
    216                                 if(table.config.debug) { benchmark("Building cache for " + totalRows + " rows:", cacheTime); }
    217                                
    218                                 return cache;
    219                         };
    220                        
    221                         function getElementText(config,node) {
    222                                
    223                                 if(!node) return "";
    224                                                                
    225                                 var t = "";
    226                                
    227                                 if(config.textExtraction == "simple") {
    228                                         if(node.childNodes[0] && node.childNodes[0].hasChildNodes()) {
    229                                                 t = node.childNodes[0].innerHTML;
    230                                         } else {
    231                                                 t = node.innerHTML;
    232                                         }
    233                                 } else {
    234                                         if(typeof(config.textExtraction) == "function") {
    235                                                 t = config.textExtraction(node);
    236                                         } else {
    237                                                 t = $(node).text();
    238                                         }       
    239                                 }
    240                                 return t;
    241                         }
    242                        
    243                         function appendToTable(table,cache) {
    244                                
    245                                 if(table.config.debug) {var appendTime = new Date()}
    246                                
    247                                 var c = cache,
    248                                         r = c.row,
    249                                         n= c.normalized,
    250                                         totalRows = n.length,
    251                                         checkCell = (n[0].length-1),
    252                                         tableBody = $(table.tBodies[0]),
    253                                         rows = [];
    254                                
    255                                 for (var i=0;i < totalRows; i++) {
    256                                         rows.push(r[n[i][checkCell]]); 
    257                                         if(!table.config.appender) {
    258                                                
    259                                                 var o = r[n[i][checkCell]];
    260                                                 var l = o.length;
    261                                                 for(var j=0; j < l; j++) {
     502            function applyWidget(table) {
     503                var c = table.config.widgets;
     504                var l = c.length;
     505                for (var i = 0; i < l; i++) {
     506
     507                    getWidgetById(c[i]).format(table);
     508                }
     509
     510            }
     511
     512            function getWidgetById(name) {
     513                var l = widgets.length;
     514                for (var i = 0; i < l; i++) {
     515                    if (widgets[i].id.toLowerCase() == name.toLowerCase()) {
     516                        return widgets[i];
     517                    }
     518                }
     519            };
     520
     521            function formatSortingOrder(v) {
     522                if (typeof(v) != "Number") {
     523                    return (v.toLowerCase() == "desc") ? 1 : 0;
     524                } else {
     525                    return (v == 1) ? 1 : 0;
     526                }
     527            }
     528
     529            function isValueInArray(v, a) {
     530                var l = a.length;
     531                for (var i = 0; i < l; i++) {
     532                    if (a[i][0] == v) {
     533                        return true;
     534                    }
     535                }
     536                return false;
     537            }
     538
     539            function setHeadersCss(table, $headers, list, css) {
     540                // remove all header information
     541                $headers.removeClass(css[0]).removeClass(css[1]);
     542
     543                var h = [];
     544                $headers.each(function (offset) {
     545                    if (!this.sortDisabled) {
     546                        h[this.column] = $(this);
     547                    }
     548                });
     549
     550                var l = list.length;
     551                for (var i = 0; i < l; i++) {
     552                    h[list[i][0]].addClass(css[list[i][1]]);
     553                }
     554            }
     555
     556            function fixColumnWidth(table, $headers) {
     557                var c = table.config;
     558                if (c.widthFixed) {
     559                    var colgroup = $('<colgroup>');
     560                    $("tr:first td", table.tBodies[0]).each(function () {
     561                        colgroup.append($('<col>').css('width', $(this).width()));
     562                    });
     563                    $(table).prepend(colgroup);
     564                };
     565            }
     566
     567            function updateHeaderSortCount(table, sortList) {
     568                var c = table.config,
     569                    l = sortList.length;
     570                for (var i = 0; i < l; i++) {
     571                    var s = sortList[i],
     572                        o = c.headerList[s[0]];
     573                    o.count = s[1];
     574                    o.count++;
     575                }
     576            }
     577
     578            /* sorting methods */
     579
     580            function multisort(table, sortList, cache) {
     581
     582                if (table.config.debug) {
     583                    var sortTime = new Date();
     584                }
     585
     586                var dynamicExp = "var sortWrapper = function(a,b) {",
     587                    l = sortList.length;
     588
     589                // TODO: inline functions.
     590                for (var i = 0; i < l; i++) {
     591
     592                    var c = sortList[i][0];
     593                    var order = sortList[i][1];
     594                    // var s = (getCachedSortType(table.config.parsers,c) == "text") ?
     595                    // ((order == 0) ? "sortText" : "sortTextDesc") : ((order == 0) ?
     596                    // "sortNumeric" : "sortNumericDesc");
     597                    // var s = (table.config.parsers[c].type == "text") ? ((order == 0)
     598                    // ? makeSortText(c) : makeSortTextDesc(c)) : ((order == 0) ?
     599                    // makeSortNumeric(c) : makeSortNumericDesc(c));
     600                    var s = (table.config.parsers[c].type == "text") ? ((order == 0) ? makeSortFunction("text", "asc", c) : makeSortFunction("text", "desc", c)) : ((order == 0) ? makeSortFunction("numeric", "asc", c) : makeSortFunction("numeric", "desc", c));
     601                    var e = "e" + i;
     602
     603                    dynamicExp += "var " + e + " = " + s; // + "(a[" + c + "],b[" + c
     604                    // + "]); ";
     605                    dynamicExp += "if(" + e + ") { return " + e + "; } ";
     606                    dynamicExp += "else { ";
     607
     608                }
     609
     610                // if value is the same keep orignal order
     611                var orgOrderCol = cache.normalized[0].length - 1;
     612                dynamicExp += "return a[" + orgOrderCol + "]-b[" + orgOrderCol + "];";
     613
     614                for (var i = 0; i < l; i++) {
     615                    dynamicExp += "}; ";
     616                }
     617
     618                dynamicExp += "return 0; ";
     619                dynamicExp += "}; ";
     620
     621                if (table.config.debug) {
     622                    benchmark("Evaling expression:" + dynamicExp, new Date());
     623                }
     624
     625                eval(dynamicExp);
     626
     627                cache.normalized.sort(sortWrapper);
     628
     629                if (table.config.debug) {
     630                    benchmark("Sorting on " + sortList.toString() + " and dir " + order + " time:", sortTime);
     631                }
     632
     633                return cache;
     634            };
     635
     636            function makeSortFunction(type, direction, index) {
     637                var a = "a[" + index + "]",
     638                    b = "b[" + index + "]";
     639                if (type == 'text' && direction == 'asc') {
     640                    return "(" + a + " == " + b + " ? 0 : (" + a + " === null ? Number.POSITIVE_INFINITY : (" + b + " === null ? Number.NEGATIVE_INFINITY : (" + a + " < " + b + ") ? -1 : 1 )));";
     641                } else if (type == 'text' && direction == 'desc') {
     642                    return "(" + a + " == " + b + " ? 0 : (" + a + " === null ? Number.POSITIVE_INFINITY : (" + b + " === null ? Number.NEGATIVE_INFINITY : (" + b + " < " + a + ") ? -1 : 1 )));";
     643                } else if (type == 'numeric' && direction == 'asc') {
     644                    return "(" + a + " === null && " + b + " === null) ? 0 :(" + a + " === null ? Number.POSITIVE_INFINITY : (" + b + " === null ? Number.NEGATIVE_INFINITY : " + a + " - " + b + "));";
     645                } else if (type == 'numeric' && direction == 'desc') {
     646                    return "(" + a + " === null && " + b + " === null) ? 0 :(" + a + " === null ? Number.POSITIVE_INFINITY : (" + b + " === null ? Number.NEGATIVE_INFINITY : " + b + " - " + a + "));";
     647                }
     648            };
     649
     650            function makeSortText(i) {
     651                return "((a[" + i + "] < b[" + i + "]) ? -1 : ((a[" + i + "] > b[" + i + "]) ? 1 : 0));";
     652            };
     653
     654            function makeSortTextDesc(i) {
     655                return "((b[" + i + "] < a[" + i + "]) ? -1 : ((b[" + i + "] > a[" + i + "]) ? 1 : 0));";
     656            };
     657
     658            function makeSortNumeric(i) {
     659                return "a[" + i + "]-b[" + i + "];";
     660            };
     661
     662            function makeSortNumericDesc(i) {
     663                return "b[" + i + "]-a[" + i + "];";
     664            };
     665
     666            function sortText(a, b) {
     667                if (table.config.sortLocaleCompare) return a.localeCompare(b);
     668                return ((a < b) ? -1 : ((a > b) ? 1 : 0));
     669            };
     670
     671            function sortTextDesc(a, b) {
     672                if (table.config.sortLocaleCompare) return b.localeCompare(a);
     673                return ((b < a) ? -1 : ((b > a) ? 1 : 0));
     674            };
     675
     676            function sortNumeric(a, b) {
     677                return a - b;
     678            };
     679
     680            function sortNumericDesc(a, b) {
     681                return b - a;
     682            };
     683
     684            function getCachedSortType(parsers, i) {
     685                return parsers[i].type;
     686            }; /* public methods */
     687            this.construct = function (settings) {
     688                return this.each(function () {
     689                    // if no thead or tbody quit.
     690                    if (!this.tHead || !this.tBodies) return;
     691                    // declare
     692                    var $this, $document, $headers, cache, config, shiftDown = 0,
     693                        sortOrder;
     694                    // new blank config object
     695                    this.config = {};
     696                    // merge and extend.
     697                    config = $.extend(this.config, $.tablesorter.defaults, settings);
     698                    // store common expression for speed
     699                    $this = $(this);
     700                    // save the settings where they read
     701                    $.data(this, "tablesorter", config);
     702                    // build headers
     703                    $headers = buildHeaders(this);
     704                    // try to auto detect column type, and store in tables config
     705                    this.config.parsers = buildParserCache(this, $headers);
     706                    // build the cache for the tbody cells
     707                    cache = buildCache(this);
     708                    // get the css class names, could be done else where.
     709                    var sortCSS = [config.cssDesc, config.cssAsc];
     710                    // fixate columns if the users supplies the fixedWidth option
     711                    fixColumnWidth(this);
     712                    // apply event handling to headers
     713                    // this is to big, perhaps break it out?
     714                    $headers.click(
     715
     716                    function (e) {
     717                        var totalRows = ($this[0].tBodies[0] && $this[0].tBodies[0].rows.length) || 0;
     718                        if (!this.sortDisabled && totalRows > 0) {
     719                            // Only call sortStart if sorting is
     720                            // enabled.
     721                            $this.trigger("sortStart");
     722                            // store exp, for speed
     723                            var $cell = $(this);
     724                            // get current column index
     725                            var i = this.column;
     726                            // get current column sort order
     727                            this.order = this.count++ % 2;
     728                                                        // always sort on the locked order.
     729                                                        if(this.lockedOrder) this.order = this.lockedOrder;
    262730                                                       
    263                                                         tableBody[0].appendChild(o[j]);
    264                                                
    265                                                 }
    266                                                
    267                                                 //tableBody.append(r[n[i][checkCell]]);
    268                                         }
    269                                 }       
    270                                
    271                                 if(table.config.appender) {
    272                                
    273                                         table.config.appender(table,rows);     
    274                                 }
    275                                
    276                                 rows = null;
    277                                
    278                                 if(table.config.debug) { benchmark("Rebuilt table:", appendTime); }
    279                                                                
    280                                 //apply table widgets
    281                                 applyWidget(table);
    282                                
    283                                 // trigger sortend
    284                                 setTimeout(function() {
    285                                         $(table).trigger("sortEnd");   
    286                                 },0);
    287                                
    288                         };
    289                        
    290                         function buildHeaders(table) {
    291                                
    292                                 if(table.config.debug) { var time = new Date(); }
    293                                
    294                                 var meta = ($.metadata) ? true : false, tableHeadersRows = [];
    295                        
    296                                 for(var i = 0; i < table.tHead.rows.length; i++) { tableHeadersRows[i]=0; };
    297                                
    298                                 $tableHeaders = $("thead th",table);
    299                
    300                                 $tableHeaders.each(function(index) {
    301                                                        
    302                                         this.count = 0;
    303                                         this.column = index;
    304                                         this.order = formatSortingOrder(table.config.sortInitialOrder);
    305                                        
    306                                         if(checkHeaderMetadata(this) || checkHeaderOptions(table,index)) this.sortDisabled = true;
    307                                        
    308                                         if(!this.sortDisabled) {
    309                                                 $(this).addClass(table.config.cssHeader);
    310                                         }
    311                                        
    312                                         // add cell to headerList
    313                                         table.config.headerList[index]= this;
    314                                 });
    315                                
    316                                 if(table.config.debug) { benchmark("Built headers:", time); log($tableHeaders); }
    317                                
    318                                 return $tableHeaders;
    319                                
    320                         };
    321                                                
    322                         function checkCellColSpan(table, rows, row) {
    323                 var arr = [], r = table.tHead.rows, c = r[row].cells;
    324                                
    325                                 for(var i=0; i < c.length; i++) {
    326                                         var cell = c[i];
    327                                        
    328                                         if ( cell.colSpan > 1) {
    329                                                 arr = arr.concat(checkCellColSpan(table, headerArr,row++));
    330                                         } else  {
    331                                                 if(table.tHead.length == 1 || (cell.rowSpan > 1 || !r[row+1])) {
    332                                                         arr.push(cell);
    333                                                 }
    334                                                 //headerArr[row] = (i+row);
    335                                         }
    336                                 }
    337                                 return arr;
    338                         };
    339                        
    340                         function checkHeaderMetadata(cell) {
    341                                 if(($.metadata) && ($(cell).metadata().sorter === false)) { return true; };
    342                                 return false;
    343                         }
    344                        
    345                         function checkHeaderOptions(table,i) { 
    346                                 if((table.config.headers[i]) && (table.config.headers[i].sorter === false)) { return true; };
    347                                 return false;
    348                         }
    349                        
    350                         function applyWidget(table) {
    351                                 var c = table.config.widgets;
    352                                 var l = c.length;
    353                                 for(var i=0; i < l; i++) {
    354                                        
    355                                         getWidgetById(c[i]).format(table);
    356                                 }
    357                                
    358                         }
    359                        
    360                         function getWidgetById(name) {
    361                                 var l = widgets.length;
    362                                 for(var i=0; i < l; i++) {
    363                                         if(widgets[i].id.toLowerCase() == name.toLowerCase() ) {
    364                                                 return widgets[i];
    365                                         }
    366                                 }
    367                         };
    368                        
    369                         function formatSortingOrder(v) {
    370                                
    371                                 if(typeof(v) != "Number") {
    372                                         i = (v.toLowerCase() == "desc") ? 1 : 0;
    373                                 } else {
    374                                         i = (v == (0 || 1)) ? v : 0;
    375                                 }
    376                                 return i;
    377                         }
    378                        
    379                         function isValueInArray(v, a) {
    380                                 var l = a.length;
    381                                 for(var i=0; i < l; i++) {
    382                                         if(a[i][0] == v) {
    383                                                 return true;   
    384                                         }
    385                                 }
    386                                 return false;
    387                         }
    388                                
    389                         function setHeadersCss(table,$headers, list, css) {
    390                                 // remove all header information
    391                                 $headers.removeClass(css[0]).removeClass(css[1]);
    392                                
    393                                 var h = [];
    394                                 $headers.each(function(offset) {
    395                                                 if(!this.sortDisabled) {
    396                                                         h[this.column] = $(this);                                       
    397                                                 }
    398                                 });
    399                                
    400                                 var l = list.length;
    401                                 for(var i=0; i < l; i++) {
    402                                         h[list[i][0]].addClass(css[list[i][1]]);
    403                                 }
    404                         }
    405                        
    406                         function fixColumnWidth(table,$headers) {
    407                                 var c = table.config;
    408                                 if(c.widthFixed) {
    409                                         var colgroup = $('<colgroup>');
    410                                         $("tr:first td",table.tBodies[0]).each(function() {
    411                                                 colgroup.append($('<col>').css('width',$(this).width()));
    412                                         });
    413                                         $(table).prepend(colgroup);
    414                                 };
    415                         }
    416                        
    417                         function updateHeaderSortCount(table,sortList) {
    418                                 var c = table.config, l = sortList.length;
    419                                 for(var i=0; i < l; i++) {
    420                                         var s = sortList[i], o = c.headerList[s[0]];
    421                                         o.count = s[1];
    422                                         o.count++;
    423                                 }
    424                         }
    425                        
    426                         /* sorting methods */
    427                         function multisort(table,sortList,cache) {
    428                                
    429                                 if(table.config.debug) { var sortTime = new Date(); }
    430                                
    431                                 var dynamicExp = "var sortWrapper = function(a,b) {", l = sortList.length;
    432                                        
    433                                 for(var i=0; i < l; i++) {
    434                                        
    435                                         var c = sortList[i][0];
    436                                         var order = sortList[i][1];
    437                                         var s = (getCachedSortType(table.config.parsers,c) == "text") ? ((order == 0) ? "sortText" : "sortTextDesc") : ((order == 0) ? "sortNumeric" : "sortNumericDesc");
    438                                        
    439                                         var e = "e" + i;
    440                                        
    441                                         dynamicExp += "var " + e + " = " + s + "(a[" + c + "],b[" + c + "]); ";
    442                                         dynamicExp += "if(" + e + ") { return " + e + "; } ";
    443                                         dynamicExp += "else { ";
    444                                 }
    445                                
    446                                 // if value is the same keep orignal order     
    447                                 var orgOrderCol = cache.normalized[0].length - 1;
    448                                 dynamicExp += "return a[" + orgOrderCol + "]-b[" + orgOrderCol + "];";
    449                                                
    450                                 for(var i=0; i < l; i++) {
    451                                         dynamicExp += "}; ";
    452                                 }
    453                                
    454                                 dynamicExp += "return 0; ";     
    455                                 dynamicExp += "}; ";   
    456                                
    457                                 eval(dynamicExp);
    458                                
    459                                 cache.normalized.sort(sortWrapper);
    460                                
    461                                 if(table.config.debug) { benchmark("Sorting on " + sortList.toString() + " and dir " + order+ " time:", sortTime); }
    462                                
    463                                 return cache;
    464                         };
    465                        
    466                         function sortText(a,b) {
    467                                 return ((a < b) ? -1 : ((a > b) ? 1 : 0));
    468                         };
    469                        
    470                         function sortTextDesc(a,b) {
    471                                 return ((b < a) ? -1 : ((b > a) ? 1 : 0));
    472                         };     
    473                        
    474                         function sortNumeric(a,b) {
    475                                 return a-b;
    476                         };
    477                        
    478                         function sortNumericDesc(a,b) {
    479                                 return b-a;
    480                         };
    481                        
    482                         function getCachedSortType(parsers,i) {
    483                                 return parsers[i].type;
    484                         };
    485                        
    486                         /* public methods */
    487                         this.construct = function(settings) {
    488 
    489                                 return this.each(function() {
    490                                        
    491                                         if(!this.tHead || !this.tBodies) return;
    492                                        
    493                                         var $this, $document,$headers, cache, config, shiftDown = 0, sortOrder;
    494                                        
    495                                         this.config = {};
    496                                        
    497                                         config = $.extend(this.config, $.tablesorter.defaults, settings);
    498                                        
    499                                         // store common expression for speed                                   
    500                                         $this = $(this);
    501                                        
    502                                         // build headers
    503                                         $headers = buildHeaders(this);
    504                                        
    505                                         // try to auto detect column type, and store in tables config
    506                                         this.config.parsers = buildParserCache(this,$headers);
    507                                        
    508                                        
    509                                         // build the cache for the tbody cells
    510                                         cache = buildCache(this);
    511                                        
    512                                         // get the css class names, could be done else where.
    513                                         var sortCSS = [config.cssDesc,config.cssAsc];
    514                                        
    515                                         // fixate columns if the users supplies the fixedWidth option
    516                                         fixColumnWidth(this);
    517                                        
    518                                         // apply event handling to headers
    519                                         // this is to big, perhaps break it out?
    520                                         $headers.click(function(e) {
    521                                                
    522                                                 $this.trigger("sortStart");
    523                                                
    524                                                 var totalRows = ($this[0].tBodies[0] && $this[0].tBodies[0].rows.length) || 0;
    525                                                
    526                                                 if(!this.sortDisabled && totalRows > 0) {
    527                                                        
    528                                                        
    529                                                         // store exp, for speed
    530                                                         var $cell = $(this);
    531        
    532                                                         // get current column index
    533                                                         var i = this.column;
    534                                                        
    535                                                         // get current column sort order
    536                                                         this.order = this.count++ % 2;
    537                                                        
    538                                                         // user only whants to sort on one column
    539                                                         if(!e[config.sortMultiSortKey]) {
    540                                                                
    541                                                                 // flush the sort list
    542                                                                 config.sortList = [];
    543                                                                
    544                                                                 if(config.sortForce != null) {
    545                                                                         var a = config.sortForce;
    546                                                                         for(var j=0; j < a.length; j++) {
    547                                                                                 if(a[j][0] != i) {
    548                                                                                         config.sortList.push(a[j]);
    549                                                                                 }
    550                                                                         }
    551                                                                 }
    552                                                                
    553                                                                 // add column to sort list
    554                                                                 config.sortList.push([i,this.order]);
    555                                                        
    556                                                         // multi column sorting
    557                                                         } else {
    558                                                                 // the user has clicked on an all ready sortet column.
    559                                                                 if(isValueInArray(i,config.sortList)) { 
    560                                                                        
    561                                                                         // revers the sorting direction for all tables.
    562                                                                         for(var j=0; j < config.sortList.length; j++) {
    563                                                                                 var s = config.sortList[j], o = config.headerList[s[0]];
    564                                                                                 if(s[0] == i) {
    565                                                                                         o.count = s[1];
    566                                                                                         o.count++;
    567                                                                                         s[1] = o.count % 2;
    568                                                                                 }
    569                                                                         }       
    570                                                                 } else {
    571                                                                         // add column to sort list array
    572                                                                         config.sortList.push([i,this.order]);
    573                                                                 }
    574                                                         };
    575                                                         setTimeout(function() {
    576                                                                 //set css for headers
    577                                                                 setHeadersCss($this[0],$headers,config.sortList,sortCSS);
    578                                                                 appendToTable($this[0],multisort($this[0],config.sortList,cache));
    579                                                         },1);
    580                                                         // stop normal event by returning false
    581                                                         return false;
    582                                                 }
    583                                         // cancel selection     
    584                                         }).mousedown(function() {
    585                                                 if(config.cancelSelection) {
    586                                                         this.onselectstart = function() {return false};
    587                                                         return false;
    588                                                 }
    589                                         });
    590                                        
    591                                         // apply easy methods that trigger binded events
    592                                         $this.bind("update",function() {
    593                                                
    594                                                 // rebuild parsers.
    595                                                 this.config.parsers = buildParserCache(this,$headers);
    596                                                
    597                                                 // rebuild the cache map
    598                                                 cache = buildCache(this);
    599                                                
    600                                         }).bind("sorton",function(e,list) {
    601                                                
    602                                                 $(this).trigger("sortStart");
    603                                                
    604                                                 config.sortList = list;
    605                                                
    606                                                 // update and store the sortlist
    607                                                 var sortList = config.sortList;
    608                                                
    609                                                 // update header count index
    610                                                 updateHeaderSortCount(this,sortList);
    611                                                
    612                                                 //set css for headers
    613                                                 setHeadersCss(this,$headers,sortList,sortCSS);
    614                                                
    615                                                
    616                                                 // sort the table and append it to the dom
    617                                                 appendToTable(this,multisort(this,sortList,cache));
    618 
    619                                         }).bind("appendCache",function() {
    620                                                
    621                                                 appendToTable(this,cache);
    622                                        
    623                                         }).bind("applyWidgetId",function(e,id) {
    624                                                
    625                                                 getWidgetById(id).format(this);
    626                                                
    627                                         }).bind("applyWidgets",function() {
    628                                                 // apply widgets
    629                                                 applyWidget(this);
    630                                         });
    631                                        
    632                                         if($.metadata && ($(this).metadata() && $(this).metadata().sortlist)) {
    633                                                 config.sortList = $(this).metadata().sortlist;
    634                                         }
    635                                         // if user has supplied a sort list to constructor.
    636                                         if(config.sortList.length > 0) {
    637                                                 $this.trigger("sorton",[config.sortList]);     
    638                                         }
    639                                        
    640                                         // apply widgets
    641                                         applyWidget(this);
    642                                 });
    643                         };
    644                        
    645                         this.addParser = function(parser) {
    646                                 var l = parsers.length, a = true;
    647                                 for(var i=0; i < l; i++) {
    648                                         if(parsers[i].id.toLowerCase() == parser.id.toLowerCase()) {
    649                                                 a = false;
    650                                         }
    651                                 }
    652                                 if(a) { parsers.push(parser); };
    653                         };
    654                        
    655                         this.addWidget = function(widget) {
    656                                 widgets.push(widget);
    657                         };
    658                        
    659                         this.formatFloat = function(s) {
    660                                 var i = parseFloat(s);
    661                                 return (isNaN(i)) ? 0 : i;
    662                         };
    663                         this.formatInt = function(s) {
    664                                 var i = parseInt(s);
    665                                 return (isNaN(i)) ? 0 : i;
    666                         };
    667                        
    668                         this.isDigit = function(s,config) {
    669                                 var DECIMAL = '\\' + config.decimal;
    670                                 var exp = '/(^[+]?0(' + DECIMAL +'0+)?$)|(^([-+]?[1-9][0-9]*)$)|(^([-+]?((0?|[1-9][0-9]*)' + DECIMAL +'(0*[1-9][0-9]*)))$)|(^[-+]?[1-9]+[0-9]*' + DECIMAL +'0+$)/';
    671                                 return RegExp(exp).test($.trim(s));
    672                         };
    673                        
    674                         this.clearTableBody = function(table) {
    675                                 if($.browser.msie) {
    676                                         function empty() {
    677                                                 while ( this.firstChild ) this.removeChild( this.firstChild );
    678                                         }
    679                                         empty.apply(table.tBodies[0]);
    680                                 } else {
    681                                         table.tBodies[0].innerHTML = "";
    682                                 }
    683                         };
    684                 }
    685         });
    686        
    687         // extend plugin scope
    688         $.fn.extend({
     731                                                        // user only whants to sort on one
     732                            // column
     733                            if (!e[config.sortMultiSortKey]) {
     734                                // flush the sort list
     735                                config.sortList = [];
     736                                if (config.sortForce != null) {
     737                                    var a = config.sortForce;
     738                                    for (var j = 0; j < a.length; j++) {
     739                                        if (a[j][0] != i) {
     740                                            config.sortList.push(a[j]);
     741                                        }
     742                                    }
     743                                }
     744                                // add column to sort list
     745                                config.sortList.push([i, this.order]);
     746                                // multi column sorting
     747                            } else {
     748                                // the user has clicked on an all
     749                                // ready sortet column.
     750                                if (isValueInArray(i, config.sortList)) {
     751                                    // revers the sorting direction
     752                                    // for all tables.
     753                                    for (var j = 0; j < config.sortList.length; j++) {
     754                                        var s = config.sortList[j],
     755                                            o = config.headerList[s[0]];
     756                                        if (s[0] == i) {
     757                                            o.count = s[1];
     758                                            o.count++;
     759                                            s[1] = o.count % 2;
     760                                        }
     761                                    }
     762                                } else {
     763                                    // add column to sort list array
     764                                    config.sortList.push([i, this.order]);
     765                                }
     766                            };
     767                            setTimeout(function () {
     768                                // set css for headers
     769                                setHeadersCss($this[0], $headers, config.sortList, sortCSS);
     770                                appendToTable(
     771                                        $this[0], multisort(
     772                                        $this[0], config.sortList, cache)
     773                                                                );
     774                            }, 1);
     775                            // stop normal event by returning false
     776                            return false;
     777                        }
     778                        // cancel selection
     779                    }).mousedown(function () {
     780                        if (config.cancelSelection) {
     781                            this.onselectstart = function () {
     782                                return false
     783                            };
     784                            return false;
     785                        }
     786                    });
     787                    // apply easy methods that trigger binded events
     788                    $this.bind("update", function () {
     789                        var me = this;
     790                        setTimeout(function () {
     791                            // rebuild parsers.
     792                            me.config.parsers = buildParserCache(
     793                            me, $headers);
     794                            // rebuild the cache map
     795                            cache = buildCache(me);
     796                        }, 1);
     797                    }).bind("updateCell", function (e, cell) {
     798                        var config = this.config;
     799                        // get position from the dom.
     800                        var pos = [(cell.parentNode.rowIndex - 1), cell.cellIndex];
     801                        // update cache
     802                        cache.normalized[pos[0]][pos[1]] = config.parsers[pos[1]].format(
     803                        getElementText(config, cell), cell);
     804                    }).bind("sorton", function (e, list) {
     805                        $(this).trigger("sortStart");
     806                        config.sortList = list;
     807                        // update and store the sortlist
     808                        var sortList = config.sortList;
     809                        // update header count index
     810                        updateHeaderSortCount(this, sortList);
     811                        // set css for headers
     812                        setHeadersCss(this, $headers, sortList, sortCSS);
     813                        // sort the table and append it to the dom
     814                        appendToTable(this, multisort(this, sortList, cache));
     815                    }).bind("appendCache", function () {
     816                        appendToTable(this, cache);
     817                    }).bind("applyWidgetId", function (e, id) {
     818                        getWidgetById(id).format(this);
     819                    }).bind("applyWidgets", function () {
     820                        // apply widgets
     821                        applyWidget(this);
     822                    });
     823                    if ($.metadata && ($(this).metadata() && $(this).metadata().sortlist)) {
     824                        config.sortList = $(this).metadata().sortlist;
     825                    }
     826                    // if user has supplied a sort list to constructor.
     827                    if (config.sortList.length > 0) {
     828                        $this.trigger("sorton", [config.sortList]);
     829                    }
     830                    // apply widgets
     831                    applyWidget(this);
     832                });
     833            };
     834            this.addParser = function (parser) {
     835                var l = parsers.length,
     836                    a = true;
     837                for (var i = 0; i < l; i++) {
     838                    if (parsers[i].id.toLowerCase() == parser.id.toLowerCase()) {
     839                        a = false;
     840                    }
     841                }
     842                if (a) {
     843                    parsers.push(parser);
     844                };
     845            };
     846            this.addWidget = function (widget) {
     847                widgets.push(widget);
     848            };
     849            this.formatFloat = function (s) {
     850                var i = parseFloat(s);
     851                return (isNaN(i)) ? 0 : i;
     852            };
     853            this.formatInt = function (s) {
     854                var i = parseInt(s);
     855                return (isNaN(i)) ? 0 : i;
     856            };
     857            this.isDigit = function (s, config) {
     858                // replace all an wanted chars and match.
     859                return /^[-+]?\d*$/.test($.trim(s.replace(/[,.']/g, '')));
     860            };
     861            this.clearTableBody = function (table) {
     862                if ($.browser.msie) {
     863                    function empty() {
     864                        while (this.firstChild)
     865                        this.removeChild(this.firstChild);
     866                    }
     867                    empty.apply(table.tBodies[0]);
     868                } else {
     869                    table.tBodies[0].innerHTML = "";
     870                }
     871            };
     872        }
     873    });
     874
     875    // extend plugin scope
     876    $.fn.extend({
    689877        tablesorter: $.tablesorter.construct
    690         });
    691        
    692         var ts = $.tablesorter;
    693        
    694         // add default parsers
    695         ts.addParser({
    696                 id: "text",
    697                 is: function(s) {
    698                         return true;
    699                 },
    700                 format: function(s) {
    701                         return $.trim(s.toLowerCase());
    702                 },
    703                 type: "text"
    704         });
    705        
    706         ts.addParser({
    707                 id: "digit",
    708                 is: function(s,table) {
    709                         var c = table.config;
    710                         return $.tablesorter.isDigit(s,c);
    711                 },
    712                 format: function(s) {
    713                         return $.tablesorter.formatFloat(s);
    714                 },
    715                 type: "numeric"
    716         });
    717        
    718         ts.addParser({
    719                 id: "currency",
    720                 is: function(s) {
    721                         return /^[£$€?.]/.test(s);
    722                 },
    723                 format: function(s) {
    724                         return $.tablesorter.formatFloat(s.replace(new RegExp(/[^0-9.]/g),""));
    725                 },
    726                 type: "numeric"
    727         });
    728        
    729         ts.addParser({
    730                 id: "ipAddress",
    731                 is: function(s) {
    732                         return /^\d{2,3}[\.]\d{2,3}[\.]\d{2,3}[\.]\d{2,3}$/.test(s);
    733                 },
    734                 format: function(s) {
    735                         var a = s.split("."), r = "", l = a.length;
    736                         for(var i = 0; i < l; i++) {
    737                                 var item = a[i];
    738                                 if(item.length == 2) {
    739                                         r += "0" + item;
    740                                 } else {
    741                                         r += item;
    742                                 }
    743                         }
    744                         return $.tablesorter.formatFloat(r);
    745                 },
    746                 type: "numeric"
    747         });
    748        
    749         ts.addParser({
    750                 id: "url",
    751                 is: function(s) {
    752                         return /^(https?|ftp|file):\/\/$/.test(s);
    753                 },
    754                 format: function(s) {
    755                         return jQuery.trim(s.replace(new RegExp(/(https?|ftp|file):\/\//),''));
    756                 },
    757                 type: "text"
    758         });
    759        
    760         ts.addParser({
    761                 id: "isoDate",
    762                 is: function(s) {
    763                         return /^\d{4}[\/-]\d{1,2}[\/-]\d{1,2}$/.test(s);
    764                 },
    765                 format: function(s) {
    766                         return $.tablesorter.formatFloat((s != "") ? new Date(s.replace(new RegExp(/-/g),"/")).getTime() : "0");
    767                 },
    768                 type: "numeric"
    769         });
    770                
    771         ts.addParser({
    772                 id: "percent",
    773                 is: function(s) {
    774                         return /\%$/.test($.trim(s));
    775                 },
    776                 format: function(s) {
    777                         return $.tablesorter.formatFloat(s.replace(new RegExp(/%/g),""));
    778                 },
    779                 type: "numeric"
    780         });
    781 
    782         ts.addParser({
    783                 id: "usLongDate",
    784                 is: function(s) {
    785                         return s.match(new RegExp(/^[A-Za-z]{3,10}\.? [0-9]{1,2}, ([0-9]{4}|'?[0-9]{2}) (([0-2]?[0-9]:[0-5][0-9])|([0-1]?[0-9]:[0-5][0-9]\s(AM|PM)))$/));
    786                 },
    787                 format: function(s) {
    788                         return $.tablesorter.formatFloat(new Date(s).getTime());
    789                 },
    790                 type: "numeric"
    791         });
    792 
    793         ts.addParser({
    794                 id: "shortDate",
    795                 is: function(s) {
    796                         return /\d{1,2}[\/\-]\d{1,2}[\/\-]\d{2,4}/.test(s);
    797                 },
    798                 format: function(s,table) {
    799                         var c = table.config;
    800                         s = s.replace(/\-/g,"/");
    801                         if(c.dateFormat == "us") {
    802                                 // reformat the string in ISO format
    803                                 s = s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})/, "$3/$1/$2");
    804                         } else if(c.dateFormat == "uk") {
    805                                 //reformat the string in ISO format
    806                                 s = s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})/, "$3/$2/$1");
    807                         } else if(c.dateFormat == "dd/mm/yy" || c.dateFormat == "dd-mm-yy") {
    808                                 s = s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{2})/, "$1/$2/$3");     
    809                         }
    810                         return $.tablesorter.formatFloat(new Date(s).getTime());
    811                 },
    812                 type: "numeric"
    813         });
    814 
    815         ts.addParser({
    816             id: "time",
    817             is: function(s) {
    818                 return /^(([0-2]?[0-9]:[0-5][0-9])|([0-1]?[0-9]:[0-5][0-9]\s(am|pm)))$/.test(s);
    819             },
    820             format: function(s) {
    821                 return $.tablesorter.formatFloat(new Date("2000/01/01 " + s).getTime());
    822             },
    823           type: "numeric"
    824         });
    825        
    826        
    827         ts.addParser({
    828             id: "metadata",
    829             is: function(s) {
    830                 return false;
    831             },
    832             format: function(s,table,cell) {
    833                         var c = table.config, p = (!c.parserMetadataName) ? 'sortValue' : c.parserMetadataName;
    834                 return $(cell).metadata()[p];
    835             },
    836           type: "numeric"
    837         });
    838        
    839         // add default widgets
    840         ts.addWidget({
    841                 id: "zebra",
    842                 format: function(table) {
    843                         if(table.config.debug) { var time = new Date(); }
    844                         $("tr:visible",table.tBodies[0])
    845                 .filter(':even')
    846                 .removeClass(table.config.widgetZebra.css[1]).addClass(table.config.widgetZebra.css[0])
    847                 .end().filter(':odd')
    848                 .removeClass(table.config.widgetZebra.css[0]).addClass(table.config.widgetZebra.css[1]);
    849                         if(table.config.debug) { $.tablesorter.benchmark("Applying Zebra widget", time); }
    850                 }
    851         });     
     878    });
     879
     880    // make shortcut
     881    var ts = $.tablesorter;
     882
     883    // add default parsers
     884    ts.addParser({
     885        id: "text",
     886        is: function (s) {
     887            return true;
     888        }, format: function (s) {
     889            return $.trim(s.toLocaleLowerCase());
     890        }, type: "text"
     891    });
     892
     893    ts.addParser({
     894        id: "digit",
     895        is: function (s, table) {
     896            var c = table.config;
     897            return $.tablesorter.isDigit(s, c);
     898        }, format: function (s) {
     899            return $.tablesorter.formatFloat(s);
     900        }, type: "numeric"
     901    });
     902
     903    ts.addParser({
     904        id: "currency",
     905        is: function (s) {
     906            return /^[£$€?.]/.test(s);
     907        }, format: function (s) {
     908            return $.tablesorter.formatFloat(s.replace(new RegExp(/[£$€]/g), ""));
     909        }, type: "numeric"
     910    });
     911
     912    ts.addParser({
     913        id: "ipAddress",
     914        is: function (s) {
     915            return /^\d{2,3}[\.]\d{2,3}[\.]\d{2,3}[\.]\d{2,3}$/.test(s);
     916        }, format: function (s) {
     917            var a = s.split("."),
     918                r = "",
     919                l = a.length;
     920            for (var i = 0; i < l; i++) {
     921                var item = a[i];
     922                if (item.length == 2) {
     923                    r += "0" + item;
     924                } else {
     925                    r += item;
     926                }
     927            }
     928            return $.tablesorter.formatFloat(r);
     929        }, type: "numeric"
     930    });
     931
     932    ts.addParser({
     933        id: "url",
     934        is: function (s) {
     935            return /^(https?|ftp|file):\/\/$/.test(s);
     936        }, format: function (s) {
     937            return jQuery.trim(s.replace(new RegExp(/(https?|ftp|file):\/\//), ''));
     938        }, type: "text"
     939    });
     940
     941    ts.addParser({
     942        id: "isoDate",
     943        is: function (s) {
     944            return /^\d{4}[\/-]\d{1,2}[\/-]\d{1,2}$/.test(s);
     945        }, format: function (s) {
     946            return $.tablesorter.formatFloat((s != "") ? new Date(s.replace(
     947            new RegExp(/-/g), "/")).getTime() : "0");
     948        }, type: "numeric"
     949    });
     950
     951    ts.addParser({
     952        id: "percent",
     953        is: function (s) {
     954            return /\%$/.test($.trim(s));
     955        }, format: function (s) {
     956            return $.tablesorter.formatFloat(s.replace(new RegExp(/%/g), ""));
     957        }, type: "numeric"
     958    });
     959
     960    ts.addParser({
     961        id: "usLongDate",
     962        is: function (s) {
     963            return s.match(new RegExp(/^[A-Za-z]{3,10}\.? [0-9]{1,2}, ([0-9]{4}|'?[0-9]{2}) (([0-2]?[0-9]:[0-5][0-9])|([0-1]?[0-9]:[0-5][0-9]\s(AM|PM)))$/));
     964        }, format: function (s) {
     965            return $.tablesorter.formatFloat(new Date(s).getTime());
     966        }, type: "numeric"
     967    });
     968
     969    ts.addParser({
     970        id: "shortDate",
     971        is: function (s) {
     972            return /\d{1,2}[\/\-]\d{1,2}[\/\-]\d{2,4}/.test(s);
     973        }, format: function (s, table) {
     974            var c = table.config;
     975            s = s.replace(/\-/g, "/");
     976            if (c.dateFormat == "us") {
     977                // reformat the string in ISO format
     978                s = s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})/, "$3/$1/$2");
     979            } else if (c.dateFormat == "uk") {
     980                // reformat the string in ISO format
     981                s = s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})/, "$3/$2/$1");
     982            } else if (c.dateFormat == "dd/mm/yy" || c.dateFormat == "dd-mm-yy") {
     983                s = s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{2})/, "$1/$2/$3");
     984            }
     985            return $.tablesorter.formatFloat(new Date(s).getTime());
     986        }, type: "numeric"
     987    });
     988    ts.addParser({
     989        id: "time",
     990        is: function (s) {
     991            return /^(([0-2]?[0-9]:[0-5][0-9])|([0-1]?[0-9]:[0-5][0-9]\s(am|pm)))$/.test(s);
     992        }, format: function (s) {
     993            return $.tablesorter.formatFloat(new Date("2000/01/01 " + s).getTime());
     994        }, type: "numeric"
     995    });
     996    ts.addParser({
     997        id: "metadata",
     998        is: function (s) {
     999            return false;
     1000        }, format: function (s, table, cell) {
     1001            var c = table.config,
     1002                p = (!c.parserMetadataName) ? 'sortValue' : c.parserMetadataName;
     1003            return $(cell).metadata()[p];
     1004        }, type: "numeric"
     1005    });
     1006    // add default widgets
     1007    ts.addWidget({
     1008        id: "zebra",
     1009        format: function (table) {
     1010            if (table.config.debug) {
     1011                var time = new Date();
     1012            }
     1013            var $tr, row = -1,
     1014                odd;
     1015            // loop through the visible rows
     1016            $("tr:visible", table.tBodies[0]).each(function (i) {
     1017                $tr = $(this);
     1018                // style children rows the same way the parent
     1019                // row was styled
     1020                if (!$tr.hasClass(table.config.cssChildRow)) row++;
     1021                odd = (row % 2 == 0);
     1022                $tr.removeClass(
     1023                table.config.widgetZebra.css[odd ? 0 : 1]).addClass(
     1024                table.config.widgetZebra.css[odd ? 1 : 0])
     1025            });
     1026            if (table.config.debug) {
     1027                $.tablesorter.benchmark("Applying Zebra widget", time);
     1028            }
     1029        }
     1030    });
    8521031})(jQuery);
Note: See TracChangeset for help on using the changeset viewer.