xref: /plugin/sortablejs/script.js (revision 12db27a468764f96a8959c2c6bd29045d3ddb615)
1/*
2  SortTable
3  version 2.1
4  7th April 2007
5  Stuart Langridge, http://www.kryogenix.org/code/browser/sorttable/
6
7  19 Feb 2008
8  Fixed some jslint errors to support DokuWiki (http://www.splitbrain.org) js compression
9
10  function reinitsort()
11  sorttable.reinit
12  added by Otto Vainio to allow sort tables updated with javascript.
13  Otto Vainio (otto@valjakko.net)
14
15  27.11.2008
16  Changed line 77 document.getElementsByTagName('table') to div.getElementsByTagName('table')
17  To allow multiple sortable tables in same page
18  (Thanks to Hans Sampiemon)
19
20  14.1.2009
21  Added option for default sorting.
22  Use dokuwiki event registration.
23
24  27.1.2009
25  Cleaned some jlint errors to make this workable, when css+js compress is set in dokuwiki
26
27  10.5.2011
28 * version 2.5 Fixed problems with secionediting, footnotes and edittable
29
30  18.7.2013
31 * version 2.6 Added support for jQuery and dokuwiki Weatherwax ->
32
33  28.5.2014
34  * version 2.7 Fixed problem with first row not getting sorted
35
36  30.5.2014
37  * version 2.8 Fixed problem with first row not getting sorted in default sort. Added option "sumrow" to prevent sum line sort.
38
39  13.8.2014
40  * version 2.9 Fixed problem with header row being sorted in earlier versions of dokuwiki. Added option for sorting back to default
41
42
43  Instructions:
44  Used from dokuwiki
45  Click on the headers to sort
46
47  Thanks to many, many people for contributions and suggestions.
48  Licenced as X11: http://www.kryogenix.org/code/browser/licence.html
49  This basically means: do what you want with it.
50*/
51
52var stIsIE = /*@cc_on!@*/false;
53var tableid = 0;
54
55sorttable = {
56  reinit: function() {
57    arguments.callee.done = true;
58    // kill the timer
59    //if (_timer) {clearInterval(_timer);}
60
61    if (!document.createElement || !document.getElementsByTagName) {return;}
62
63//    sorttable.DATE_RE = /^(\d\d?)[\/\.\-](\d\d?)[\/\.\-]((\d\d)?\d\d)$/;
64    sorttable.DATE_RE = /^(\d\d?)[\/\.\-](\d\d?)[\/\.\-]((\d\d)?\d\d)( (\d\d?)[:\.]?(\d\d?))?$/;
65
66
67    forEach(document.getElementsByTagName('table'), function(table) {
68      if (table.className.search(/\bsortable\b/) != -1) {
69        sorttable.makeSortable(table);
70      }
71    });
72    forEach(document.getElementsByTagName('div'), function(div) {
73      if (div.className.search(/\bsortable\b/) != -1) {
74        sorttable.makeSortablediv(div);
75      }
76    });
77  },
78
79  init: function() {
80    // quit if this function has already been called
81    if (arguments.callee.done) {return;}
82    // flag this function so we don't do the same thing twice
83    arguments.callee.done = true;
84    // kill the timer
85    //if (_timer) {clearInterval(_timer);}
86
87    if (!document.createElement || !document.getElementsByTagName) {return;}
88
89//    sorttable.DATE_RE = /^(\d\d?)[\/\.\-](\d\d?)[\/\.\-]((\d\d)?\d\d)$/;
90    sorttable.DATE_RE = /^(\d\d?)[\/\.\-](\d\d?)[\/\.\-]((\d\d)?\d\d)( (\d\d?):?(\d\d?))?$/;
91
92    forEach(document.getElementsByTagName('table'), function(table) {
93      if (table.className.search(/\bsortable\b/) != -1) {
94        sorttable.makeSortable(table);
95      }
96    });
97    forEach(document.getElementsByTagName('div'), function(div) {
98      if (div.className.search(/\bsortable\b/) != -1) {
99        sorttable.makeSortablediv(div);
100      }
101    });
102
103  },
104  makeSortablediv: function(div) {
105        if (div.getElementsByTagName('table').length === 0) {
106        } else {
107          forEach(div.getElementsByTagName('table'), function(table) {
108            colid=div.className;
109            //overs = new Array();
110            var patt1=/\bcol_\d_[a-z]+/gi;
111            var overs = new Array();
112            if (colid.search(patt1) != -1) {
113              var overrides = new Array();
114              overrides = colid.match(patt1);
115              var xo="";
116              for (xo in overrides)
117              {
118                if (xo == "")
119                {
120                } else {
121                  try
122                  {
123                    var tmp = overrides[xo].split("_");
124                    var ind = tmp[1];
125                    var val = tmp[2];
126                    overs[ind]=val;
127
128                  }
129                  catch (e)
130                  {
131                  }
132                }
133              }
134              colid = colid.replace(patt1,'');
135            }
136            var patt2=/\bsortbottom_?\d?/gi;
137            var bottoms = 0;
138            if (colid.search(patt2) != -1) {
139              var bs = new Array();
140              bs = colid.match(patt2);
141              try
142              {
143                var tmp = bs[0].split("_");
144                //var ind = tmp[1];
145                var val=1;
146                if(tmp.length>1) {
147                  val = tmp[1];
148                }
149                bottoms=val;
150              }
151              catch (e)
152              {
153              }
154            }
155            var patt2ph=/\bthreephase/gi;
156            var ph2=true;
157            if (colid.search(patt2ph) != -1) {
158              ph2=false;
159            }
160
161            sorttable.makeSortable(table,overs,bottoms,ph2);
162            var pattdefault=/\bsortr?\d/gi;
163            if (colid.search(pattdefault) != -1) {
164              var mi= new Array();
165              mi = colid.match(pattdefault);
166              colid = mi[0].replace('sort','');
167              if (!colid != '')
168              {
169                colid = colid.trim();
170              }
171              revs=false;
172              if (colid.search(/\br/) != -1) {
173                revs=true;
174                colid = colid.replace('r','');
175              }
176              sorttable.defaultSort(table,colid,revs);
177            }
178          });
179        }
180  },
181  defaultSort: function(table, colid, revs) {
182//    theadrow = table.tHead.rows[0].cells;
183    havetHead = table.tHead;
184    var sindex=1;
185    if (havetHead) {
186      sindex=0;
187    }
188    theadrow = table.rows[0].cells;
189    colid--;
190    colname ="col"+colid;
191     // remove sorttable_sorted classes
192     var thiscell=false;
193     forEach(theadrow, function(cell) {
194       colclass=cell.className;
195       classname = colclass.split(" ");
196       if (classname[0]==colname)
197//       if (cell.className==colname)
198       {
199         thiscell=cell;
200       }
201//       if (cell.nodeType == 1) { // an element
202//         cell.className = cell.className.replace('sorttable_sorted_reverse','');
203//         cell.className = cell.className.replace('sorttable_sorted','');
204//       }
205     });
206     if (thiscell===false) {return;}
207     sortfwdind = document.getElementById('sorttable_sortfwdind');
208     if (sortfwdind) { sortfwdind.parentNode.removeChild(sortfwdind); }
209     sortrevind = document.getElementById('sorttable_sortrevind');
210     if (sortrevind) { sortrevind.parentNode.removeChild(sortrevind); }
211
212     thiscell.className += ' sorttable_sorted';
213     sortfwdind = document.createElement('span');
214     sortfwdind.id = "sorttable_sortfwdind";
215     sortfwdind.innerHTML = stIsIE ? '&nbsp<font face="webdings">6</font>' : '&nbsp;&#x25BE;';
216     thiscell.appendChild(sortfwdind);
217
218     // build an array to sort. This is a Schwartzian transform thing,
219     // i.e., we "decorate" each row with the actual sort key,
220     // sort based on the sort keys, and then put the rows back in order
221     // which is a lot faster because you only do getInnerText once per row
222     row_array = [];
223     col = thiscell.sorttable_columnindex;
224     rows = thiscell.sorttable_tbody.rows;
225     for (var j=sindex; j<rows.length; j++) {
226       row_array[row_array.length] = [sorttable.getInnerText(rows[j].cells[col]), rows[j]];
227     }
228     /* If you want a stable sort, uncomment the following line */
229     //sorttable.shaker_sort(row_array, this.sorttable_sortfunction);
230     /* and comment out this one */
231     row_array.sort(thiscell.sorttable_sortfunction);
232
233     tb = thiscell.sorttable_tbody;
234     for (var jj=0; jj<row_array.length; jj++) {
235       tb.appendChild(row_array[jj][1]);
236     }
237
238     delete row_array;
239     // If reverse sort wanted, then doit
240     if (revs) {
241      // reverse the table, which is quicker
242       sorttable.reverse(thiscell.sorttable_tbody, sindex);
243       thiscell.className = thiscell.className.replace('sorttable_sorted',
244                                                       'sorttable_sorted_reverse');
245       thiscell.removeChild(document.getElementById('sorttable_sortfwdind'));
246       sortrevind = document.createElement('span');
247       sortrevind.id = "sorttable_sortrevind";
248       sortrevind.innerHTML = stIsIE ? '&nbsp<font face="webdings">5</font>' : '&nbsp;&#x25B4;';
249       thiscell.appendChild(sortrevind);
250     }
251
252
253
254  },
255
256  makeSortable: function(table,overrides, bottoms, ph2) {
257//    tableid++;
258/*
259    if (table.getElementsByTagName('thead').length === 0) {
260      // table doesn't have a tHead. Since it should have, create one and
261      // put the first table row in it.
262      the = document.createElement('thead');
263      the.appendChild(table.rows[0]);
264      table.insertBefore(the,table.firstChild);
265    }
266*/
267    // Safari doesn't support table.tHead, sigh
268/*
269    if (table.tHead === null) {table.tHead = table.getElementsByTagName('thead')[0];}
270
271    if (table.tHead.rows.length != 1) {return;} // can't cope with two header rows
272  */
273//    table.tHead.className += ' tableid'+tableid;
274
275    // Sorttable v1 put rows with a class of "sortbottom" at the bottom (as
276    // "total" rows, for example). This is B&R, since what you're supposed
277    // to do is put them in a tfoot. So, if there are sortbottom rows,
278    // for backwards compatibility, move them to tfoot (creating it if needed).
279
280    sortbottomrows = [];
281    if (bottoms>0) {
282    frombottom=table.rows.length-bottoms;
283    for (var i=table.rows.length-1; i>=frombottom; i--) {
284//      if (bottoms<frombottom) {
285        sortbottomrows[sortbottomrows.length] = table.rows[i];
286//      }
287//      frombottom++;
288    }
289    if (sortbottomrows) {
290      if (table.tFoot === null) {
291      // table doesn't have a tfoot. Create one.
292      tfo = document.createElement('tfoot');
293      table.appendChild(tfo);
294      }
295      for (var ii=sortbottomrows.length-1; ii>=0; ii--) {
296        tfo.appendChild(sortbottomrows[ii]);
297      }
298      delete sortbottomrows;
299    }
300    }
301    // work through each column and calculate its type
302    havetHead = table.tHead;
303    var sindex=1;
304    if (havetHead) {
305      sindex=0;
306    }
307    headrow = table.rows[0].cells;
308//    for (var i=0; i<headrow.length; i++) {
309    for (i=0; i<headrow.length; i++) {
310      // manually override the type with a sorttable_type attribute
311      var colOptions="";
312      if (overrides[i+1])
313      {
314        colOptions=overrides[i+1];
315      }
316      if (!colOptions.match(/\bnosort\b/)) { // skip this col
317        mtch = colOptions.match(/\b[a-z0-9]+\b/);
318        if (mtch) { override = mtch[0]; }
319        if (mtch && typeof sorttable["sort_"+override] == 'function') {
320          headrow[i].sorttable_sortfunction = sorttable["sort_"+override];
321        } else {
322          headrow[i].sorttable_sortfunction = sorttable.guessType(table,i);
323        }
324/*
325      if (!headrow[i].className.match(/\bsorttable_nosort\b/)) { // skip this col
326        mtch = headrow[i].className.match(/\bsorttable_([a-z0-9]+)\b/);
327        if (mtch) { override = mtch[1]; }
328        if (mtch && typeof sorttable["sort_"+override] == 'function') {
329          headrow[i].sorttable_sortfunction = sorttable["sort_"+override];
330        } else {
331          headrow[i].sorttable_sortfunction = sorttable.guessType(table,i);
332        }
333*/
334        // make it clickable to sort
335        headrow[i].sorttable_columnindex = i;
336        headrow[i].sorttable_tbody = table.tBodies[0];
337        headrow[i].sindex = sindex;
338//        dean_addEvent(headrow[i],"click", function(e) {
339//        addEvent(headrow[i],"click", function(e) {
340        jQuery(headrow[i]).click(function(){
341
342          theadrow = this.parentNode;
343
344          if (this.className.search(/\bsorttable_sorted\b/) != -1) {
345            // if we're already sorted by this column, just
346            // reverse the table, which is quicker
347            sorttable.reverse(this.sorttable_tbody,this.sindex);
348            this.className = this.className.replace('sorttable_sorted',
349                                                    'sorttable_sorted_reverse');
350            sortfwdind = document.getElementById('sorttable_sortfwdind');
351            if (sortfwdind) { sortfwdind.parentNode.removeChild(sortfwdind); }
352//            this.removeChild(document.getElementById('sorttable_sortfwdind'));
353            sortrevind = document.getElementById('sorttable_sortrevind');
354            if (sortrevind) { sortrevind.parentNode.removeChild(sortrevind); }
355            sortrevind = document.createElement('span');
356            sortrevind.id = "sorttable_sortrevind";
357            sortrevind.innerHTML = stIsIE ? '&nbsp<font face="webdings">5</font>' : '&nbsp;&#x25B4;';
358            this.appendChild(sortrevind);
359            return;
360          }
361          if (this.className.search(/\bsorttable_sorted_reverse\b/) != -1) {
362            if (ph2==false) {
363              sorttable.original_order(this.sorttable_tbody,this.sindex);
364              forEach(theadrow.childNodes, function(cell) {
365                if (cell.nodeType == 1) { // an element
366                  cell.className = cell.className.replace('sorttable_sorted_reverse','');
367                  cell.className = cell.className.replace('sorttable_sorted','');
368                }
369              });
370              sortfwdind = document.getElementById('sorttable_sortfwdind');
371              if (sortfwdind) { sortfwdind.parentNode.removeChild(sortfwdind); }
372              sortrevind = document.getElementById('sorttable_sortrevind');
373              if (sortrevind) { sortrevind.parentNode.removeChild(sortrevind); }
374              return;
375            } else {
376              // if we're already sorted by this column in reverse, just
377              // re-reverse the table, which is quicker
378              sorttable.reverse(this.sorttable_tbody,this.sindex);
379              this.className = this.className.replace('sorttable_sorted_reverse',
380                                                      'sorttable_sorted');
381              sortrevind = document.getElementById('sorttable_sortrevind');
382              if (sortrevind) { sortrevind.parentNode.removeChild(sortrevind); }
383  //            this.removeChild(document.getElementById('sorttable_sortrevind'));
384              sortfwdind = document.getElementById('sorttable_sortfwdind');
385              if (sortfwdind) { sortfwdind.parentNode.removeChild(sortfwdind); }
386              sortfwdind = document.createElement('span');
387              sortfwdind.id = "sorttable_sortfwdind";
388              sortfwdind.innerHTML = stIsIE ? '&nbsp<font face="webdings">6</font>' : '&nbsp;&#x25BE;';
389              this.appendChild(sortfwdind);
390              return;
391            }
392          }
393
394          // remove sorttable_sorted classes
395//          theadrow = this.parentNode;
396          forEach(theadrow.childNodes, function(cell) {
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) { sortfwdind.parentNode.removeChild(sortfwdind); }
404          sortrevind = document.getElementById('sorttable_sortrevind');
405          if (sortrevind) { sortrevind.parentNode.removeChild(sortrevind); }
406
407          this.className += ' sorttable_sorted';
408          sortfwdind = document.createElement('span');
409          sortfwdind.id = "sorttable_sortfwdind";
410          sortfwdind.innerHTML = stIsIE ? '&nbsp<font face="webdings">6</font>' : '&nbsp;&#x25BE;';
411          this.appendChild(sortfwdind);
412
413          // build an array to sort. This is a Schwartzian transform thing,
414          // i.e., we "decorate" each row with the actual sort key,
415          // sort based on the sort keys, and then put the rows back in order
416          // which is a lot faster because you only do getInnerText once per row
417          row_array = [];
418          col = this.sorttable_columnindex;
419          rows = this.sorttable_tbody.rows;
420          sindex = this.sindex;
421          for (var j=sindex; j<rows.length; j++) {
422            row_array[row_array.length] = [sorttable.getInnerText(rows[j].cells[col]), rows[j]];
423          }
424          /* If you want a stable sort, uncomment the following line */
425          //sorttable.shaker_sort(row_array, this.sorttable_sortfunction);
426          /* and comment out this one */
427          row_array.sort(this.sorttable_sortfunction);
428
429          tb = this.sorttable_tbody;
430          for (var j3=0; j3<row_array.length; j3++) {
431            tb.appendChild(row_array[j3][1]);
432          }
433
434          delete row_array;
435        });
436      }
437    }
438  },
439
440  guessType: function(table, column) {
441    // guess the type of a column based on its first non-blank row
442  var NONE=0;
443  var TEXT=0;
444  var NUM=0;
445  var DDMM=0;
446  var MMDD=0;
447    sortfn = sorttable.sort_alpha;
448    for (var i=0; i<table.tBodies[0].rows.length; i++) {
449      text = sorttable.getInnerText(table.tBodies[0].rows[i].cells[column]);
450      set=0;
451      if (text !== '') {
452        if (text.match(/^-?[£$¤]?[\d,.]+[%€]?$/)) {
453          set=1;
454          NUM=1;
455        }
456        // check for a date: dd/mm/yyyy or dd/mm/yy
457        // can have / or . or - as separator
458        // can be mm/dd as well
459        possdate = text.match(sorttable.DATE_RE);
460        if (possdate) {
461          // looks like a date
462          first = parseInt(possdate[1]);
463          second = parseInt(possdate[2]);
464          if (first > 12) {
465            // definitely dd/mm
466//            return sorttable.sort_ddmm;
467            set=1;
468            DDMM=1;
469          } else if (second > 12) {
470            set=1;
471            MMDD=1;
472//            return sorttable.sort_mmdd;
473          } else {
474            // looks like a date, but we can't tell which, so assume
475            // that it's dd/mm (English imperialism!) and keep looking
476            set=1;
477            DDMM=1;
478//            sortfn = sorttable.sort_ddmm;
479          }
480        }
481        // if nothing known then assume text
482        if (set==0) {
483          TEXT=1;
484        }
485        set=0;
486
487      }
488    }
489    if (TEXT>0 || NUM+DDMM+MMDD>1) return sorttable.sort_alpha;
490    if (NUM>0) return sorttable.sort_numeric;
491    if (DDMM>0) return sorttable.sort_ddmm;
492    if (MMDD>0) return sorttable.sort_mmdd;
493  },
494
495  getInnerText: function(node) {
496    // gets the text we want to use for sorting for a cell.
497    // strips leading and trailing whitespace.
498    // this is *not* a generic getInnerText function; it's special to sorttable.
499    // for example, you can override the cell text with a customkey attribute.
500    // it also gets .value for <input> fields.
501
502    hasInputs = (typeof node.getElementsByTagName == 'function') &&
503                 node.getElementsByTagName('input').length;
504
505    if (node.getAttribute("sorttable_customkey") !== null) {
506      return node.getAttribute("sorttable_customkey");
507    }
508    else if (typeof node.textContent != 'undefined' && !hasInputs) {
509      return node.textContent.replace(/^\s+|\s+$/g, '');
510    }
511    else if (typeof node.innerText != 'undefined' && !hasInputs) {
512      return node.innerText.replace(/^\s+|\s+$/g, '');
513    }
514    else if (typeof node.text != 'undefined' && !hasInputs) {
515      return node.text.replace(/^\s+|\s+$/g, '');
516    }
517    else {
518      switch (node.nodeType) {
519        case 3:
520          if (node.nodeName.toLowerCase() == 'input') {
521            return node.value.replace(/^\s+|\s+$/g, '');
522          }
523        case 4:
524          return node.nodeValue.replace(/^\s+|\s+$/g, '');
525          break;
526        case 1:
527        case 11:
528          var innerText = '';
529          for (var i = 0; i < node.childNodes.length; i++) {
530            innerText += sorttable.getInnerText(node.childNodes[i]);
531          }
532          return innerText.replace(/^\s+|\s+$/g, '');
533          break;
534        default:
535          return '';
536      }
537    }
538  },
539
540  reverse: function(tbody,sindex) {
541    // reverse the rows in a tbody
542    newrows = [];
543    for (var i=sindex; i<tbody.rows.length; i++) {
544      newrows[newrows.length] = tbody.rows[i];
545    }
546    for (var i=newrows.length-1; i>=0; i--) {
547       tbody.appendChild(newrows[i]);
548    }
549    delete newrows;
550  },
551  original_order: function(tbody,isindex) {
552    // build an array to sort. This is a Schwartzian transform thing,
553    // i.e., we "decorate" each row with the actual sort key,
554    // sort based on the sort keys, and then put the rows back in order
555    // which is a lot faster because you only do getInnerText once per row
556    row_array = [];
557    rows = tbody.rows;
558    sindex = isindex;
559    for (var j=sindex; j<rows.length; j++) {
560      row_array[row_array.length] = [rows[j].className, rows[j]];
561    }
562    /* If you want a stable sort, uncomment the following line */
563    //sorttable.shaker_sort(row_array, this.sorttable_sortfunction);
564    /* and comment out this one */
565    row_array.sort(sorttable.sort_alpha);
566
567    tb = tbody;
568    for (var j3=0; j3<row_array.length; j3++) {
569      tb.appendChild(row_array[j3][1]);
570    }
571
572    delete row_array;
573  },
574
575  /* sort functions
576     each sort function takes two parameters, a and b
577     you are comparing a[0] and b[0] */
578  sort_numeric: function(a,b) {
579    aa = parseFloat(a[0].replace(/[^0-9.\-]/g,''));
580    if (isNaN(aa)) {aa = 0;}
581    bb = parseFloat(b[0].replace(/[^0-9.\-]/g,''));
582    if (isNaN(bb)) {bb = 0;}
583    return aa-bb;
584  },
585  sort_alpha: function(a,b) {
586    if (a[0]==b[0]) {return 0;}
587    if (a[0]<b[0]) {return -1;}
588    return 1;
589  },
590  sort_ddmm: function(a,b) {
591    mtch = a[0].match(sorttable.DATE_RE);
592    y = mtch[3]; m = mtch[2]; d = mtch[1];
593    t = mtch[5]+'';
594    if (t.length < 1 ) {t = '';}
595    if (m.length == 1) {m = '0'+m;}
596    if (d.length == 1) {d = '0'+d;}
597    dt1 = y+m+d+t;
598    mtch = b[0].match(sorttable.DATE_RE);
599    y = mtch[3]; m = mtch[2]; d = mtch[1];
600    t = mtch[5]+'';
601    if (t.length < 1 ) {t = '';}
602    if (m.length == 1) {m = '0'+m;}
603    if (d.length == 1) {d = '0'+d;}
604    dt2 = y+m+d+t;
605    if (dt1==dt2) {return 0;}
606    if (dt1<dt2) {return -1;}
607    return 1;
608  },
609  sort_mmdd: function(a,b) {
610    mtch = a[0].match(sorttable.DATE_RE);
611    y = mtch[3]; d = mtch[2]; m = mtch[1];
612    t = mtch[5]+'';
613    if (m.length == 1) {m = '0'+m;}
614    if (d.length == 1) {d = '0'+d;}
615    dt1 = y+m+d+t;
616    mtch = b[0].match(sorttable.DATE_RE);
617    y = mtch[3]; d = mtch[2]; m = mtch[1];
618    t = mtch[5]+'';
619    if (t.length < 1 ) {t = '';}
620    if (m.length == 1) {m = '0'+m;}
621    if (d.length == 1) {d = '0'+d;}
622    dt2 = y+m+d+t;
623    if (dt1==dt2) {return 0;}
624    if (dt1<dt2) {return -1;}
625    return 1;
626  },
627
628  shaker_sort: function(list, comp_func) {
629    // A stable sort function to allow multi-level sorting of data
630    // see: http://en.wikipedia.org/wiki/Cocktail_sort
631    // thanks to Joseph Nahmias
632    var b = 0;
633    var t = list.length - 1;
634    var swap = true;
635
636    while(swap) {
637        swap = false;
638        for(var i = b; i < t; ++i) {
639            if ( comp_func(list[i], list[i+1]) > 0 ) {
640                var q = list[i]; list[i] = list[i+1]; list[i+1] = q;
641                swap = true;
642            }
643        } // for
644        t--;
645
646        if (!swap) {break;}
647
648        for(var i = t; i > b; --i) {
649            if ( comp_func(list[i], list[i-1]) < 0 ) {
650                var q = list[i]; list[i] = list[i-1]; list[i-1] = q;
651                swap = true;
652            }
653        } // for
654        b++;
655
656    } // while(swap)
657  }
658
659
660};
661/* ******************************************************************
662   Supporting functions: bundled here to avoid depending on a library
663   ****************************************************************** */
664
665
666
667// Dean Edwards/Matthias Miller/John Resig
668
669
670// Dean's forEach: http://dean.edwards.name/base/forEach.js
671/*
672  forEach, version 1.0
673  Copyright 2006, Dean Edwards
674  License: http://www.opensource.org/licenses/mit-license.php
675*/
676
677// array-like enumeration
678if (!Array.forEach) { // mozilla already supports this
679  Array.forEach = function(array, block, context) {
680    for (var i = 0; i < array.length; i++) {
681      block.call(context, array[i], i, array);
682    }
683  };
684}
685
686// generic enumeration
687Function.prototype.forEach = function(object, block, context) {
688  for (var key in object) {
689    if (typeof this.prototype[key] == "undefined") {
690      block.call(context, object[key], key, object);
691    }
692  }
693};
694
695// character enumeration
696String.forEach = function(string, block, context) {
697  Array.forEach(string.split(""), function(chr, index) {
698    block.call(context, chr, index, string);
699  });
700};
701
702// globally resolve forEach enumeration
703var forEach = function(object, block, context) {
704  if (object) {
705    var resolve = Object; // default
706    if (object instanceof Function) {
707      // functions have a "length" property
708      resolve = Function;
709    } else if (object.forEach instanceof Function) {
710      // the object implements a custom forEach method so use that
711      object.forEach(block, context);
712      return;
713    } else if (typeof object == "string") {
714      // the object is a string
715      resolve = String;
716    } else if (typeof object.length == "number") {
717      // the object is array-like
718      resolve = Array;
719    }
720    resolve.forEach(object, block, context);
721  }
722};
723
724
725if ('undefined' != typeof(window.addEvent)) {
726    window.addEvent(window, 'load', sorttable.init);
727} else {
728    jQuery(function() {
729      sorttable.init();
730    });
731}
732
733//sorttable.init;
734
735function reinitsort() {
736  sorttable.reinit();
737}
738