xref: /plugin/sortablejs/script.js (revision 196c4f7765cbf5c4f20d6c6e0bc5a39e6f045e5c)
1/*
2 SortTable
3
4 Instructions:
5 Used from dokuwiki
6 Click on the headers to sort
7
8 Thanks to many, many people for contributions and suggestions.
9 Currently licensed as X11:
10 http://www.kryogenix.org/code/browser/licence.html
11
12 v2.1 (7th April 2007)
13 * originally by Stuart Langridge, http://www.kryogenix.org/code/browser/sorttable/
14
15 v2.1b (19 Feb 2008)
16 * first Otto Vainio (otto@valjakko.net) revision
17 * Fixed some jslint errors to support DokuWiki (http://www.splitbrain.org) js compression
18 * function reinitsort()
19 * sorttable.reinit
20
21 v2.2 (27.11.2008)
22 * Changed line 77 document.getElementsByTagName('table') to div.getElementsByTagName('table')
23 to allow multiple sortable tables in same page (Thanks to Hans Sampiemon)
24
25 v2.3 (14.1.2009)
26 * Added option for default sorting.
27 * Use dokuwiki event registration.
28
29 v2.4 (27.1.2009)
30 * Cleaned some jlint errors to make this workable, when css+js compress is set in dokuwiki
31
32 v2.5 (10.5.2011)
33 * Fixed problems with secionediting, footnotes and edittable
34
35 v2.6 (18.7.2013)
36 * Added support for jQuery and dokuwiki Weatherwax ->
37
38 v2.7 (28.5.2014)
39 * Fixed problem with first row not getting sorted
40
41 v2.8 (30.5.2014)
42 * Fixed problem with first row not getting sorted in default sort. Added option "sumrow" to prevent sum line sort.
43
44 v2.9 (13.8.2014)
45 * Fixed problem with header row being sorted in earlier versions of dokuwiki. Added option for sorting back to default
46
47 v2.11 (6.7.2015)
48 * Added ip address sort. (Thanks Chefkeks)
49
50 v2.12 (14.12.2015)
51 * PHP 7 compatibility and issue #8. Default sort for columns > 9
52
53 v2.13
54 * revision by Vaxquis; fixes some (most) of the current issues
55
56 v2.14
57 * by Vaxquis: fixed and streamlined date sorts; it now uses the JS Date() Object to do the sorting.
58 */
59
60//
61
62var stIsIE = /*@cc_on!@*/false;
63//var tableid = 0;
64
65var sorttable = {
66    reinit: function () {
67        arguments.callee.done = true;
68        // kill the timer
69        //if (_timer) {clearInterval(_timer);}
70
71        if ( !document.createElement || !document.getElementsByTagName ) {
72            return;
73        }
74
75        var elems = document.getElementsByTagName( "table" );
76        var elem;
77        for( var i = 0; i < elems.length; i++ ) {
78            elem = elems[i];
79            if ( elem.className.search( /\bsortable\b/ ) !== -1 ) {
80                sorttable.makeSortable( elem );
81            }
82        }
83        elems = document.getElementsByTagName( "div" );
84        for( var i = 0; i < elems.length; i++ ) {
85            elem = elems[i];
86            if ( elem.className.search( /\bsortable\b/ ) !== -1 ) {
87                sorttable.makeSortableDiv( elem );
88            }
89        }
90    },
91    init: function () {
92        if ( arguments.callee.done ) {
93            return;
94        }
95        sorttable.reinit();
96    },
97    makeSortableDiv: function ( div ) {
98        var childTables = div.getElementsByTagName( "table" );
99        var elem;
100        for( var i = 0; i < childTables.length; i++ ) {
101            elem = childTables[i];
102            var colid = div.className;
103            var patt1 = /\bcol_\d_[a-z]+/gi;
104            var overs = [];
105            if ( colid.search( patt1 ) !== -1 ) {
106                var overrides = colid.match( patt1 );
107                for( var i = 0; i < overrides.length; i++ ) {
108                    var entry = overrides[i];
109                    if ( entry !== "" ) {
110                        try {
111                            var tmp = entry.split( "_" );
112                            var ind = tmp[1];
113                            var val = tmp[2];
114                            overs[ind] = val;
115                        } catch( e ) {
116                        }
117                    }
118                }
119                colid = colid.replace( patt1, '' );
120            }
121            var patt2 = /\bsortbottom_?\d?/gi;
122            var bottoms = 0;
123            if ( colid.search( patt2 ) !== -1 ) {
124                var bs = colid.match( patt2 );
125                try {
126                    var tmp = bs[0].split( "_" );
127                    //var ind = tmp[1];
128                    var val = 1;
129                    if ( tmp.length > 1 ) {
130                        val = tmp[1];
131                    }
132                    bottoms = val;
133                } catch( e ) {
134                }
135            }
136            var patt2ph = /\bthreephase/gi;
137            var ph2 = true;
138            if ( colid.search( patt2ph ) !== -1 ) {
139                ph2 = false;
140            }
141
142            sorttable.makeSortable( elem, overs, bottoms, ph2 );
143            var pattdefault = /\bsortr?\d\d?/gi;
144            if ( colid.search( pattdefault ) !== -1 ) {
145                var mi = colid.match( pattdefault );
146                colid = mi[0].replace( 'sort', '' );
147                if ( colid !== '' ) {
148                    colid = colid.trim();
149                }
150                var revs = false;
151                if ( colid.search( /\br/ ) !== -1 ) {
152                    revs = true;
153                    colid = colid.replace( 'r', '' );
154                }
155                sorttable.defaultSort( elem, colid, revs );
156            }
157        }
158    },
159    defaultSort: function ( table, colid, revs ) {
160//    theadrow = table.tHead.rows[0].cells;
161        var havetHead = table.tHead;
162        var sindex = 1;
163        if ( havetHead ) {
164            sindex = 0;
165        }
166        var theadrow = table.rows[0].cells;
167        colid--;
168        var colname = "col" + colid;
169        // remove sorttable_sorted classes
170        var thiscell = false;
171        for( var i = 0; i < theadrow.length; i++ ) {
172            var cell = theadrow[i];
173            var colclass = cell.className;
174            var classname = colclass.split( " " );
175//       if (cell.className==colname)
176            if ( classname[0] === colname ) {
177                thiscell = cell;
178            }
179//       if (cell.nodeType == 1) { // an element
180//         cell.className = cell.className.replace('sorttable_sorted_reverse','');
181//         cell.className = cell.className.replace('sorttable_sorted','');
182//       }
183        }
184        if ( thiscell === false ) {
185            return;
186        }
187        var sortfwdind = document.getElementById( 'sorttable_sortfwdind' );
188        if ( sortfwdind ) {
189            sortfwdind.parentNode.removeChild( sortfwdind );
190        }
191        var sortrevind = document.getElementById( 'sorttable_sortrevind' );
192        if ( sortrevind ) {
193            sortrevind.parentNode.removeChild( sortrevind );
194        }
195
196        thiscell.className += ' sorttable_sorted';
197        sortfwdind = document.createElement( 'span' );
198        sortfwdind.id = "sorttable_sortfwdind";
199        sortfwdind.innerHTML = stIsIE ? '&nbsp<font face="webdings">6</font>' : '&nbsp;&#x25BE;';
200        thiscell.appendChild( sortfwdind );
201
202        // build an array to sort. This is a Schwartzian transform thing,
203        // i.e., we "decorate" each row with the actual sort key,
204        // sort based on the sort keys, and then put the rows back in order
205        // which is a lot faster because you only do getInnerText once per row
206        var row_array = [];
207        var col = thiscell.sorttable_columnindex;
208        var rows = thiscell.sorttable_tbody.rows;
209        for( var j = sindex; j < rows.length; j++ ) {
210            row_array[row_array.length] = [sorttable.getInnerText( rows[j].cells[col] ), rows[j]];
211        }
212        /* If you want a stable sort, uncomment the following line */
213        //sorttable.shaker_sort(row_array, this.sorttable_sortfunction);
214        /* and comment out this one */
215        row_array.sort( thiscell.sorttable_sortfunction );
216
217        var tb = thiscell.sorttable_tbody;
218        for( var jj = 0; jj < row_array.length; jj++ ) {
219            tb.appendChild( row_array[jj][1] );
220        }
221
222        //delete row_array;
223        // If reverse sort wanted, then doit
224        if ( revs ) {
225            // reverse the table, which is quicker
226            sorttable.reverse( thiscell.sorttable_tbody, sindex );
227            thiscell.className = thiscell.className.replace( 'sorttable_sorted',
228                    'sorttable_sorted_reverse' );
229            thiscell.removeChild( document.getElementById( 'sorttable_sortfwdind' ) );
230            sortrevind = document.createElement( 'span' );
231            sortrevind.id = "sorttable_sortrevind";
232            sortrevind.innerHTML = stIsIE ? '&nbsp<font face="webdings">5</font>' : '&nbsp;&#x25B4;';
233            thiscell.appendChild( sortrevind );
234        }
235    },
236    makeSortable: function ( table, overrides, bottoms, ph2 ) {
237//    tableid++;
238        /*
239         if (table.getElementsByTagName('thead').length === 0) {
240         // table doesn't have a tHead. Since it should have, create one and
241         // put the first table row in it.
242         the = document.createElement('thead');
243         the.appendChild(table.rows[0]);
244         table.insertBefore(the,table.firstChild);
245         }
246         */
247        // Safari doesn't support table.tHead, sigh
248        /*
249         if (table.tHead === null) {table.tHead = table.getElementsByTagName('thead')[0];}
250
251         if (table.tHead.rows.length != 1) {return;} // can't cope with two header rows
252         */
253//    table.tHead.className += ' tableid'+tableid;
254
255        // Sorttable v1 put rows with a class of "sortbottom" at the bottom (as
256        // "total" rows, for example). This is B&R, since what you're supposed
257        // to do is put them in a tfoot. So, if there are sortbottom rows,
258        // for backwards compatibility, move them to tfoot (creating it if needed).
259
260        var sortbottomrows = [];
261        if ( bottoms > 0 ) {
262            var frombottom = table.rows.length - bottoms;
263            for( var i = table.rows.length - 1; i >= frombottom; i-- ) {
264//      if (bottoms<frombottom) {
265                sortbottomrows[sortbottomrows.length] = table.rows[i];
266//      }
267//      frombottom++;
268            }
269            if ( sortbottomrows ) {
270                var tfo;
271                if ( table.tFoot === null ) {
272                    // table doesn't have a tfoot. Create one.
273                    tfo = document.createElement( 'tfoot' );
274                    table.appendChild( tfo );
275                }
276                for( var ii = sortbottomrows.length - 1; ii >= 0; ii-- ) {
277                    tfo.appendChild( sortbottomrows[ii] );
278                }
279                //delete sortbottomrows;
280            }
281        }
282        // work through each column and calculate its type
283        var havetHead = table.tHead;
284        var sindex = 1;
285        if ( havetHead ) {
286            sindex = 0;
287        }
288        var headrow = table.rows[0].cells;
289//    for (var i=0; i<headrow.length; i++) {
290        for( var i = 0; i < headrow.length; i++ ) {
291            // manually override the type with a sorttable_type attribute
292            var colOptions = "";
293            if ( overrides[i + 1] )
294            {
295                colOptions = overrides[i + 1];
296            }
297            if ( !colOptions.match( /\bnosort\b/ ) ) { // skip this col
298                var mtch = colOptions.match( /\b[a-z0-9]+\b/ );
299                var override;
300                if ( mtch ) {
301                    override = mtch[0];
302                }
303                if ( mtch && typeof sorttable["sort_" + override] === 'function' ) {
304                    headrow[i].sorttable_sortfunction = sorttable["sort_" + override];
305                } else {
306                    headrow[i].sorttable_sortfunction = sorttable.guessType( table, i );
307                }
308                /*
309                 if (!headrow[i].className.match(/\bsorttable_nosort\b/)) { // skip this col
310                 mtch = headrow[i].className.match(/\bsorttable_([a-z0-9]+)\b/);
311                 if (mtch) { override = mtch[1]; }
312                 if (mtch && typeof sorttable["sort_"+override] == 'function') {
313                 headrow[i].sorttable_sortfunction = sorttable["sort_"+override];
314                 } else {
315                 headrow[i].sorttable_sortfunction = sorttable.guessType(table,i);
316                 }
317                 */
318                // make it clickable to sort
319                headrow[i].sorttable_columnindex = i;
320                headrow[i].sorttable_tbody = table.tBodies[0];
321                headrow[i].sindex = sindex;
322//        dean_addEvent(headrow[i],"click", function(e) {
323//        addEvent(headrow[i],"click", function(e) {
324                jQuery( headrow[i] ).click( function () {
325
326                    var theadrow = this.parentNode;
327                    var sortrevind, sortfwdind;
328                    if ( this.className.search( /\bsorttable_sorted\b/ ) !== -1 ) {
329                        // if we're already sorted by this column, just
330                        // reverse the table, which is quicker
331                        sorttable.reverse( this.sorttable_tbody, this.sindex );
332                        this.className = this.className.replace( 'sorttable_sorted',
333                                'sorttable_sorted_reverse' );
334                        sortfwdind = document.getElementById( 'sorttable_sortfwdind' );
335                        if ( sortfwdind ) {
336                            sortfwdind.parentNode.removeChild( sortfwdind );
337                        }
338//            this.removeChild(document.getElementById('sorttable_sortfwdind'));
339                        sortrevind = document.getElementById( 'sorttable_sortrevind' );
340                        if ( sortrevind ) {
341                            sortrevind.parentNode.removeChild( sortrevind );
342                        }
343                        sortrevind = document.createElement( 'span' );
344                        sortrevind.id = "sorttable_sortrevind";
345                        sortrevind.innerHTML = stIsIE ? '&nbsp<font face="webdings">5</font>' : '&nbsp;&#x25B4;';
346                        this.appendChild( sortrevind );
347                        return;
348                    }
349                    if ( this.className.search( /\bsorttable_sorted_reverse\b/ ) !== -1 ) {
350                        if ( !ph2 ) {
351                            sorttable.original_order( this.sorttable_tbody, this.sindex );
352                            var list = theadrow.childNodes;
353                            for( var i = 0; i < list.length; i++ ) {
354                                var cell = list[i];
355                                if ( cell.nodeType === 1 ) { // an element
356                                    cell.className = cell.className.replace( 'sorttable_sorted_reverse', '' );
357                                    cell.className = cell.className.replace( 'sorttable_sorted', '' );
358                                }
359                            }
360                            sortfwdind = document.getElementById( 'sorttable_sortfwdind' );
361                            if ( sortfwdind ) {
362                                sortfwdind.parentNode.removeChild( sortfwdind );
363                            }
364                            sortrevind = document.getElementById( 'sorttable_sortrevind' );
365                            if ( sortrevind ) {
366                                sortrevind.parentNode.removeChild( sortrevind );
367                            }
368                            return;
369                        } else {
370                            // if we're already sorted by this column in reverse, just
371                            // re-reverse the table, which is quicker
372                            sorttable.reverse( this.sorttable_tbody, this.sindex );
373                            this.className = this.className.replace( 'sorttable_sorted_reverse',
374                                    'sorttable_sorted' );
375                            sortrevind = document.getElementById( 'sorttable_sortrevind' );
376                            if ( sortrevind ) {
377                                sortrevind.parentNode.removeChild( sortrevind );
378                            }
379                            //            this.removeChild(document.getElementById('sorttable_sortrevind'));
380                            sortfwdind = document.getElementById( 'sorttable_sortfwdind' );
381                            if ( sortfwdind ) {
382                                sortfwdind.parentNode.removeChild( sortfwdind );
383                            }
384                            sortfwdind = document.createElement( 'span' );
385                            sortfwdind.id = "sorttable_sortfwdind";
386                            sortfwdind.innerHTML = stIsIE ? '&nbsp<font face="webdings">6</font>' : '&nbsp;&#x25BE;';
387                            this.appendChild( sortfwdind );
388                            return;
389                        }
390                    }
391
392                    // remove sorttable_sorted classes
393//          theadrow = this.parentNode;
394                    var list = theadrow.childNodes;
395                    for( var i = 0; i < list.length; i++ ) {
396                        var cell = list[i];
397                        if ( cell.nodeType === 1 ) { // an element
398                            cell.className = cell.className.replace( 'sorttable_sorted_reverse', '' );
399                            cell.className = cell.className.replace( 'sorttable_sorted', '' );
400                        }
401                    }
402                    sortfwdind = document.getElementById( 'sorttable_sortfwdind' );
403                    if ( sortfwdind ) {
404                        sortfwdind.parentNode.removeChild( sortfwdind );
405                    }
406                    sortrevind = document.getElementById( 'sorttable_sortrevind' );
407                    if ( sortrevind ) {
408                        sortrevind.parentNode.removeChild( sortrevind );
409                    }
410
411                    this.className += ' sorttable_sorted';
412                    sortfwdind = document.createElement( 'span' );
413                    sortfwdind.id = "sorttable_sortfwdind";
414                    sortfwdind.innerHTML = stIsIE ? '&nbsp<font face="webdings">6</font>' : '&nbsp;&#x25BE;';
415                    this.appendChild( sortfwdind );
416
417                    // build an array to sort. This is a Schwartzian transform thing,
418                    // i.e., we "decorate" each row with the actual sort key,
419                    // sort based on the sort keys, and then put the rows back in order
420                    // which is a lot faster because you only do getInnerText once per row
421                    var row_array = [];
422                    var col = this.sorttable_columnindex;
423                    var rows = this.sorttable_tbody.rows;
424                    sindex = this.sindex;
425                    for( var j = sindex; j < rows.length; j++ ) {
426                        row_array[row_array.length] = [sorttable.getInnerText( rows[j].cells[col] ), rows[j]];
427                    }
428                    /* If you want a stable sort, uncomment the following line */
429                    //sorttable.shaker_sort(row_array, this.sorttable_sortfunction);
430                    /* and comment out this one */
431                    row_array.sort( this.sorttable_sortfunction );
432
433                    var tb = this.sorttable_tbody;
434                    for( var j3 = 0; j3 < row_array.length; j3++ ) {
435                        tb.appendChild( row_array[j3][1] );
436                    }
437
438                    //delete row_array;
439                } );
440            }
441        }
442    },
443    guessType: function ( table, column ) {
444        // guess the type of a column based on its first non-blank row
445        var textCnt = 0;
446        var numCnt = 0;
447        var dateCnt = 0;
448        var ipCnt = 0;
449
450        for( var i = 0; i < table.tBodies[0].rows.length; i++ ) {
451            var text = sorttable.getInnerText( table.tBodies[0].rows[i].cells[column] );
452            if ( text !== "" ) {
453                if ( text.match( /^([01]?\d\d?|2[0-4]\d|25[0-5])\.([01]?\d\d?|2[0-4]\d|25[0-5])\.([01]?\d\d?|2[0-4]\d|25[0-5])\.([01]?\d\d?|2[0-4]\d|25[0-5])$/ ) ) {
454                    ipCnt++;
455                } else if ( text.match( /^[\-\+].?[\d,.]+.?$/ ) ) {
456                    numCnt++;
457                } else if ( isNaN( new Date( text ).getTime() ) ) {
458                    dateCnt++;
459                } else { // not a date (nor IP nor number)
460                    textCnt++;
461                }
462            }
463        }
464        if ( textCnt > numCnt && textCnt > ipCnt && textCnt > dateCnt )
465            return sorttable.sort_alpha;
466        if ( numCnt > ipCnt && numCnt > dateCnt )
467            return sorttable.sort_numeric;
468        if ( ipCnt > dateCnt )
469            return sorttable.sort_ipaddr;
470        return sorttable.sort_date;
471    },
472    getInnerText: function ( node ) {
473        // gets the text we want to use for sorting for a cell.
474        // strips leading and trailing whitespace.
475        // this is *not* a generic getInnerText function; it's special to sorttable.
476        // for example, you can override the cell text with a customkey attribute.
477        // it also gets .value for <input> fields.
478        if ( !node ) {
479            return '';
480        }
481        var hasInputs = ( typeof node.getElementsByTagName === "function" ) &&
482                node.getElementsByTagName( "input" ).length;
483        if ( node.getAttribute( "sorttable_customkey" ) !== null ) {
484            return node.getAttribute( "sorttable_customkey" );
485        } else if ( typeof node.textContent !== "undefined" && !hasInputs ) {
486            return node.textContent.replace( /^\s+|\s+$/g, '' );
487        } else if ( typeof node.innerText !== "undefined" && !hasInputs ) {
488            return node.innerText.replace( /^\s+|\s+$/g, '' );
489        } else if ( typeof node.text !== "undefined" && !hasInputs ) {
490            return node.text.replace( /^\s+|\s+$/g, '' );
491        } else {
492            switch ( node.nodeType ) {
493                case 3:
494                    return ( node.nodeName.toLowerCase() === "input" ) ? node.value.replace( /^\s+|\s+$/g, '' ) : '';
495                case 4:
496                    return node.nodeValue.replace( /^\s+|\s+$/g, '' );
497                case 1:
498                case 11:
499                    var innerText = '';
500                    for( var i = 0; i < node.childNodes.length; i++ ) {
501                        innerText += sorttable.getInnerText( node.childNodes[i] );
502                    }
503                    return innerText.replace( /^\s+|\s+$/g, '' );
504                default:
505                    return '';
506            }
507        }
508    },
509    reverse: function ( tbody, sindex ) {
510        // reverse the rows in a tbody
511        var newrows = [];
512        for( var i = sindex; i < tbody.rows.length; i++ ) {
513            newrows[newrows.length] = tbody.rows[i];
514        }
515        for( var i = newrows.length - 1; i >= 0; i-- ) {
516            tbody.appendChild( newrows[i] );
517        }
518        //delete newrows;
519    },
520    original_order: function ( tbody, isindex ) {
521        // build an array to sort. This is a Schwartzian transform thing,
522        // i.e., we "decorate" each row with the actual sort key,
523        // sort based on the sort keys, and then put the rows back in order
524        // which is a lot faster because you only do getInnerText once per row
525        var row_array = [];
526        var rows = tbody.rows;
527        var sindex = isindex;
528        for( var j = sindex; j < rows.length; j++ ) {
529            row_array[row_array.length] = [rows[j].className, rows[j]];
530        }
531        /* If you want a stable sort, uncomment the following line */
532        //sorttable.shaker_sort(row_array, this.sorttable_sortfunction);
533        /* and comment out this one */
534        row_array.sort( sorttable.sort_alpha );
535
536        var tb = tbody;
537        for( var j3 = 0; j3 < row_array.length; j3++ ) {
538            tb.appendChild( row_array[j3][1] );
539        }
540
541        //delete row_array;
542    },
543    /* sort functions
544     each sort function takes two parameters, a and b
545     you are comparing a[0] and b[0] */
546    sort_ipaddr: function ( a, b ) {
547        var aa = a[0].split( ".", 4 );
548        var bb = b[0].split( ".", 4 );
549        var resulta = aa[0] * 0x1000000 + aa[1] * 0x10000 + aa[2] * 0x100 + aa[3] * 1;
550        var resultb = bb[0] * 0x1000000 + bb[1] * 0x10000 + bb[2] * 0x100 + bb[3] * 1;
551        return resulta - resultb;
552    },
553    sort_numeric: function ( a, b ) {
554        if ( a[0] === "" ) {
555            return -1;
556        }
557        if ( b[0] === "" ) {
558            return 1;
559        }
560        var aa = parseFloat( a[0].replace( /[^0-9.\-]/g, '' ) );
561        if ( isNaN( aa ) ) {
562            aa = Number.NEGATIVE_INFINITY;
563        }
564        var bb = parseFloat( b[0].replace( /[^0-9.\-]/g, '' ) );
565        if ( isNaN( bb ) ) {
566            bb = Number.NEGATIVE_INFINITY;
567        }
568        return aa - bb;
569    },
570    sort_alpha: function ( a, b ) {
571        return a[0].localeCompare( b[0] );
572    },
573    sort_date: function ( a, b ) {
574        var aa = new Date( a[0] ), bb = new Date( b[0] );
575        return ( aa > bb ) - ( aa < bb );
576    },
577    shaker_sort: function ( list, comp_func ) {
578        // A stable sort function to allow multi-level sorting of data
579        // see: http://en.wikipedia.org/wiki/Cocktail_sort
580        // thanks to Joseph Nahmias
581        var b = 0;
582        var t = list.length - 1;
583        var swap = true;
584        var q;
585
586        while( swap ) {
587            swap = false;
588            for( var i = b; i < t; ++i ) {
589                if ( comp_func( list[i], list[i + 1] ) > 0 ) {
590                    q = list[i];
591                    list[i] = list[i + 1];
592                    list[i + 1] = q;
593                    swap = true;
594                }
595            } // for
596            t--;
597
598            if ( !swap ) {
599                break;
600            }
601
602            for( var i = t; i > b; --i ) {
603                if ( comp_func( list[i], list[i - 1] ) < 0 ) {
604                    q = list[i];
605                    list[i] = list[i - 1];
606                    list[i - 1] = q;
607                    swap = true;
608                }
609            } // for
610            b++;
611
612        } // while(swap)
613    }
614
615};
616
617if ( typeof ( window.addEvent ) !== "undefined" ) {
618    window.addEvent( window, "load", sorttable.init );
619} else {
620    jQuery( function () {
621        sorttable.init();
622    } );
623}
624