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