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