19d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee/*
29d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee  SortTable
39d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee  version 2
49d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee  7th April 2007
59d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee  Stuart Langridge, http://www.kryogenix.org/code/browser/sorttable/
69d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee
79d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee  Instructions:
89d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee  Download this file
99d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee  Add <script src="sorttable.js"></script> to your HTML
109d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee  Add class="sortable" to any table you'd like to make sortable
119d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee  Click on the headers to sort
129d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee
139d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee  Thanks to many, many people for contributions and suggestions.
149d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee  Licenced as X11: http://www.kryogenix.org/code/browser/licence.html
159d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee  This basically means: do what you want with it.
169d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee*/
179d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee
189d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee
199d2d6e14b0932b6a74e01f393d5efed61458941bBob Leevar stIsIE = /*@cc_on!@*/false;
209d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee
219d2d6e14b0932b6a74e01f393d5efed61458941bBob Leesorttable = {
229d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee  init: function() {
239d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    // quit if this function has already been called
249d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    if (arguments.callee.done) return;
259d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    // flag this function so we don't do the same thing twice
269d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    arguments.callee.done = true;
279d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    // kill the timer
289d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    if (_timer) clearInterval(_timer);
299d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee
309d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    if (!document.createElement || !document.getElementsByTagName) return;
319d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee
329d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    sorttable.DATE_RE = /^(\d\d?)[\/\.-](\d\d?)[\/\.-]((\d\d)?\d\d)$/;
339d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee
349d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    forEach(document.getElementsByTagName('table'), function(table) {
359d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee      if (table.className.search(/\bsortable\b/) != -1) {
369d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee        sorttable.makeSortable(table);
379d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee      }
389d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    });
399d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee
409d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee  },
419d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee
429d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee  makeSortable: function(table) {
439d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    if (table.getElementsByTagName('thead').length == 0) {
449d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee      // table doesn't have a tHead. Since it should have, create one and
459d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee      // put the first table row in it.
469d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee      the = document.createElement('thead');
479d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee      the.appendChild(table.rows[0]);
489d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee      table.insertBefore(the,table.firstChild);
499d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    }
509d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    // Safari doesn't support table.tHead, sigh
519d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    if (table.tHead == null) table.tHead = table.getElementsByTagName('thead')[0];
529d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee
539d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    if (table.tHead.rows.length != 1) return; // can't cope with two header rows
549d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee
559d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    // Sorttable v1 put rows with a class of "sortbottom" at the bottom (as
569d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    // "total" rows, for example). This is B&R, since what you're supposed
579d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    // to do is put them in a tfoot. So, if there are sortbottom rows,
589d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    // for backwards compatibility, move them to tfoot (creating it if needed).
599d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    sortbottomrows = [];
609d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    for (var i=0; i<table.rows.length; i++) {
619d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee      if (table.rows[i].className.search(/\bsortbottom\b/) != -1) {
629d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee        sortbottomrows[sortbottomrows.length] = table.rows[i];
639d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee      }
649d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    }
659d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    if (sortbottomrows) {
669d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee      if (table.tFoot == null) {
679d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee        // table doesn't have a tfoot. Create one.
689d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee        tfo = document.createElement('tfoot');
699d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee        table.appendChild(tfo);
709d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee      }
719d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee      for (var i=0; i<sortbottomrows.length; i++) {
729d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee        tfo.appendChild(sortbottomrows[i]);
739d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee      }
749d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee      delete sortbottomrows;
759d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    }
769d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee
779d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    // work through each column and calculate its type
789d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    headrow = table.tHead.rows[0].cells;
799d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    for (var i=0; i<headrow.length; i++) {
809d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee      // manually override the type with a sorttable_type attribute
819d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee      if (!headrow[i].className.match(/\bsorttable_nosort\b/)) { // skip this col
829d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee        mtch = headrow[i].className.match(/\bsorttable_([a-z0-9]+)\b/);
839d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee        if (mtch) { override = mtch[1]; }
849d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee	      if (mtch && typeof sorttable["sort_"+override] == 'function') {
859d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee	        headrow[i].sorttable_sortfunction = sorttable["sort_"+override];
869d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee	      } else {
879d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee	        headrow[i].sorttable_sortfunction = sorttable.guessType(table,i);
889d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee	      }
899d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee	      // make it clickable to sort
909d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee	      headrow[i].sorttable_columnindex = i;
919d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee	      headrow[i].sorttable_tbody = table.tBodies[0];
929d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee	      dean_addEvent(headrow[i],"click", function(e) {
939d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee
949d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee          if (this.className.search(/\bsorttable_sorted\b/) != -1) {
959d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee            // if we're already sorted by this column, just
969d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee            // reverse the table, which is quicker
979d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee            sorttable.reverse(this.sorttable_tbody);
989d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee            this.className = this.className.replace('sorttable_sorted',
999d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee                                                    'sorttable_sorted_reverse');
1009d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee            this.removeChild(document.getElementById('sorttable_sortfwdind'));
1019d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee            sortrevind = document.createElement('span');
1029d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee            sortrevind.id = "sorttable_sortrevind";
1039d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee            sortrevind.innerHTML = stIsIE ? '&nbsp<font face="webdings">5</font>' : '&nbsp;&#x25B4;';
1049d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee            this.appendChild(sortrevind);
1059d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee            return;
1069d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee          }
1079d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee          if (this.className.search(/\bsorttable_sorted_reverse\b/) != -1) {
1089d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee            // if we're already sorted by this column in reverse, just
1099d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee            // re-reverse the table, which is quicker
1109d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee            sorttable.reverse(this.sorttable_tbody);
1119d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee            this.className = this.className.replace('sorttable_sorted_reverse',
1129d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee                                                    'sorttable_sorted');
1139d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee            this.removeChild(document.getElementById('sorttable_sortrevind'));
1149d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee            sortfwdind = document.createElement('span');
1159d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee            sortfwdind.id = "sorttable_sortfwdind";
1169d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee            sortfwdind.innerHTML = stIsIE ? '&nbsp<font face="webdings">6</font>' : '&nbsp;&#x25BE;';
1179d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee            this.appendChild(sortfwdind);
1189d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee            return;
1199d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee          }
1209d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee
1219d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee          // remove sorttable_sorted classes
1229d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee          theadrow = this.parentNode;
1239d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee          forEach(theadrow.childNodes, function(cell) {
1249d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee            if (cell.nodeType == 1) { // an element
1259d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee              cell.className = cell.className.replace('sorttable_sorted_reverse','');
1269d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee              cell.className = cell.className.replace('sorttable_sorted','');
1279d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee            }
1289d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee          });
1299d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee          sortfwdind = document.getElementById('sorttable_sortfwdind');
1309d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee          if (sortfwdind) { sortfwdind.parentNode.removeChild(sortfwdind); }
1319d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee          sortrevind = document.getElementById('sorttable_sortrevind');
1329d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee          if (sortrevind) { sortrevind.parentNode.removeChild(sortrevind); }
1339d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee
1349d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee          this.className += ' sorttable_sorted';
1359d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee          sortfwdind = document.createElement('span');
1369d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee          sortfwdind.id = "sorttable_sortfwdind";
1379d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee          sortfwdind.innerHTML = stIsIE ? '&nbsp<font face="webdings">6</font>' : '&nbsp;&#x25BE;';
1389d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee          this.appendChild(sortfwdind);
1399d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee
1409d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee	        // build an array to sort. This is a Schwartzian transform thing,
1419d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee	        // i.e., we "decorate" each row with the actual sort key,
1429d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee	        // sort based on the sort keys, and then put the rows back in order
1439d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee	        // which is a lot faster because you only do getInnerText once per row
1449d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee	        row_array = [];
1459d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee	        col = this.sorttable_columnindex;
1469d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee	        rows = this.sorttable_tbody.rows;
1479d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee	        for (var j=0; j<rows.length; j++) {
1489d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee	          row_array[row_array.length] = [sorttable.getInnerText(rows[j].cells[col]), rows[j]];
1499d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee	        }
1509d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee	        /* If you want a stable sort, uncomment the following line */
1519d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee	        //sorttable.shaker_sort(row_array, this.sorttable_sortfunction);
1529d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee	        /* and comment out this one */
1539d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee	        row_array.sort(this.sorttable_sortfunction);
1549d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee
1559d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee	        tb = this.sorttable_tbody;
1569d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee	        for (var j=0; j<row_array.length; j++) {
1579d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee	          tb.appendChild(row_array[j][1]);
1589d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee	        }
1599d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee
1609d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee	        delete row_array;
1619d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee	      });
1629d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee	    }
1639d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    }
1649d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee  },
1659d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee
1669d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee  guessType: function(table, column) {
1679d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    // guess the type of a column based on its first non-blank row
1689d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    sortfn = sorttable.sort_alpha;
1699d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    for (var i=0; i<table.tBodies[0].rows.length; i++) {
1709d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee      text = sorttable.getInnerText(table.tBodies[0].rows[i].cells[column]);
1719d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee      if (text != '') {
1729d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee        if (text.match(/^-?[�$�]?[\d,.]+%?$/)) {
1739d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee          return sorttable.sort_numeric;
1749d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee        }
1759d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee        // check for a date: dd/mm/yyyy or dd/mm/yy
1769d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee        // can have / or . or - as separator
1779d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee        // can be mm/dd as well
1789d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee        possdate = text.match(sorttable.DATE_RE)
1799d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee        if (possdate) {
1809d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee          // looks like a date
1819d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee          first = parseInt(possdate[1]);
1829d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee          second = parseInt(possdate[2]);
1839d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee          if (first > 12) {
1849d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee            // definitely dd/mm
1859d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee            return sorttable.sort_ddmm;
1869d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee          } else if (second > 12) {
1879d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee            return sorttable.sort_mmdd;
1889d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee          } else {
1899d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee            // looks like a date, but we can't tell which, so assume
1909d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee            // that it's dd/mm (English imperialism!) and keep looking
1919d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee            sortfn = sorttable.sort_ddmm;
1929d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee          }
1939d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee        }
1949d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee      }
1959d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    }
1969d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    return sortfn;
1979d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee  },
1989d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee
1999d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee  getInnerText: function(node) {
2009d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    // gets the text we want to use for sorting for a cell.
2019d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    // strips leading and trailing whitespace.
2029d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    // this is *not* a generic getInnerText function; it's special to sorttable.
2039d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    // for example, you can override the cell text with a customkey attribute.
2049d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    // it also gets .value for <input> fields.
2059d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee
2069d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    hasInputs = (typeof node.getElementsByTagName == 'function') &&
2079d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee                 node.getElementsByTagName('input').length;
2089d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee
2099d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    if (node.getAttribute("sorttable_customkey") != null) {
2109d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee      return node.getAttribute("sorttable_customkey");
2119d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    }
2129d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    else if (typeof node.textContent != 'undefined' && !hasInputs) {
2139d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee      return node.textContent.replace(/^\s+|\s+$/g, '');
2149d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    }
2159d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    else if (typeof node.innerText != 'undefined' && !hasInputs) {
2169d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee      return node.innerText.replace(/^\s+|\s+$/g, '');
2179d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    }
2189d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    else if (typeof node.text != 'undefined' && !hasInputs) {
2199d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee      return node.text.replace(/^\s+|\s+$/g, '');
2209d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    }
2219d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    else {
2229d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee      switch (node.nodeType) {
2239d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee        case 3:
2249d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee          if (node.nodeName.toLowerCase() == 'input') {
2259d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee            return node.value.replace(/^\s+|\s+$/g, '');
2269d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee          }
2279d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee        case 4:
2289d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee          return node.nodeValue.replace(/^\s+|\s+$/g, '');
2299d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee          break;
2309d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee        case 1:
2319d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee        case 11:
2329d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee          var innerText = '';
2339d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee          for (var i = 0; i < node.childNodes.length; i++) {
2349d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee            innerText += sorttable.getInnerText(node.childNodes[i]);
2359d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee          }
2369d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee          return innerText.replace(/^\s+|\s+$/g, '');
2379d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee          break;
2389d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee        default:
2399d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee          return '';
2409d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee      }
2419d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    }
2429d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee  },
2439d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee
2449d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee  reverse: function(tbody) {
2459d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    // reverse the rows in a tbody
2469d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    newrows = [];
2479d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    for (var i=0; i<tbody.rows.length; i++) {
2489d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee      newrows[newrows.length] = tbody.rows[i];
2499d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    }
2509d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    for (var i=newrows.length-1; i>=0; i--) {
2519d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee       tbody.appendChild(newrows[i]);
2529d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    }
2539d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    delete newrows;
2549d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee  },
2559d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee
2569d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee  /* sort functions
2579d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee     each sort function takes two parameters, a and b
2589d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee     you are comparing a[0] and b[0] */
2599d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee  sort_numeric: function(a,b) {
2609d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    aa = parseFloat(a[0].replace(/[^0-9.-]/g,''));
2619d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    if (isNaN(aa)) aa = 0;
2629d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    bb = parseFloat(b[0].replace(/[^0-9.-]/g,''));
2639d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    if (isNaN(bb)) bb = 0;
2649d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    return aa-bb;
2659d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee  },
2669d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee  sort_alpha: function(a,b) {
2679d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    if (a[0]==b[0]) return 0;
2689d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    if (a[0]<b[0]) return -1;
2699d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    return 1;
2709d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee  },
2719d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee  sort_ddmm: function(a,b) {
2729d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    mtch = a[0].match(sorttable.DATE_RE);
2739d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    y = mtch[3]; m = mtch[2]; d = mtch[1];
2749d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    if (m.length == 1) m = '0'+m;
2759d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    if (d.length == 1) d = '0'+d;
2769d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    dt1 = y+m+d;
2779d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    mtch = b[0].match(sorttable.DATE_RE);
2789d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    y = mtch[3]; m = mtch[2]; d = mtch[1];
2799d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    if (m.length == 1) m = '0'+m;
2809d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    if (d.length == 1) d = '0'+d;
2819d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    dt2 = y+m+d;
2829d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    if (dt1==dt2) return 0;
2839d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    if (dt1<dt2) return -1;
2849d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    return 1;
2859d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee  },
2869d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee  sort_mmdd: function(a,b) {
2879d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    mtch = a[0].match(sorttable.DATE_RE);
2889d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    y = mtch[3]; d = mtch[2]; m = mtch[1];
2899d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    if (m.length == 1) m = '0'+m;
2909d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    if (d.length == 1) d = '0'+d;
2919d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    dt1 = y+m+d;
2929d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    mtch = b[0].match(sorttable.DATE_RE);
2939d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    y = mtch[3]; d = mtch[2]; m = mtch[1];
2949d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    if (m.length == 1) m = '0'+m;
2959d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    if (d.length == 1) d = '0'+d;
2969d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    dt2 = y+m+d;
2979d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    if (dt1==dt2) return 0;
2989d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    if (dt1<dt2) return -1;
2999d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    return 1;
3009d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee  },
3019d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee
3029d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee  shaker_sort: function(list, comp_func) {
3039d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    // A stable sort function to allow multi-level sorting of data
3049d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    // see: http://en.wikipedia.org/wiki/Cocktail_sort
3059d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    // thanks to Joseph Nahmias
3069d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    var b = 0;
3079d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    var t = list.length - 1;
3089d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    var swap = true;
3099d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee
3109d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    while(swap) {
3119d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee        swap = false;
3129d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee        for(var i = b; i < t; ++i) {
3139d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee            if ( comp_func(list[i], list[i+1]) > 0 ) {
3149d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee                var q = list[i]; list[i] = list[i+1]; list[i+1] = q;
3159d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee                swap = true;
3169d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee            }
3179d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee        } // for
3189d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee        t--;
3199d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee
3209d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee        if (!swap) break;
3219d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee
3229d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee        for(var i = t; i > b; --i) {
3239d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee            if ( comp_func(list[i], list[i-1]) < 0 ) {
3249d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee                var q = list[i]; list[i] = list[i-1]; list[i-1] = q;
3259d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee                swap = true;
3269d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee            }
3279d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee        } // for
3289d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee        b++;
3299d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee
3309d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    } // while(swap)
3319d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee  }
3329d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee}
3339d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee
3349d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee/* ******************************************************************
3359d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee   Supporting functions: bundled here to avoid depending on a library
3369d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee   ****************************************************************** */
3379d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee
3389d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee// Dean Edwards/Matthias Miller/John Resig
3399d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee
3409d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee/* for Mozilla/Opera9 */
3419d2d6e14b0932b6a74e01f393d5efed61458941bBob Leeif (document.addEventListener) {
3429d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    document.addEventListener("DOMContentLoaded", sorttable.init, false);
3439d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee}
3449d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee
3459d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee/* for Internet Explorer */
3469d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee/*@cc_on @*/
3479d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee/*@if (@_win32)
3489d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    document.write("<script id=__ie_onload defer src=javascript:void(0)><\/script>");
3499d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    var script = document.getElementById("__ie_onload");
3509d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    script.onreadystatechange = function() {
3519d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee        if (this.readyState == "complete") {
3529d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee            sorttable.init(); // call the onload handler
3539d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee        }
3549d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    };
3559d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee/*@end @*/
3569d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee
3579d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee/* for Safari */
3589d2d6e14b0932b6a74e01f393d5efed61458941bBob Leeif (/WebKit/i.test(navigator.userAgent)) { // sniff
3599d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    var _timer = setInterval(function() {
3609d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee        if (/loaded|complete/.test(document.readyState)) {
3619d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee            sorttable.init(); // call the onload handler
3629d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee        }
3639d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee    }, 10);
3649d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee}
3659d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee
3669d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee/* for other browsers */
3679d2d6e14b0932b6a74e01f393d5efed61458941bBob Leewindow.onload = sorttable.init;
3689d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee
3699d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee// written by Dean Edwards, 2005
3709d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee// with input from Tino Zijdel, Matthias Miller, Diego Perini
3719d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee
3729d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee// http://dean.edwards.name/weblog/2005/10/add-event/
3739d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee
3749d2d6e14b0932b6a74e01f393d5efed61458941bBob Leefunction dean_addEvent(element, type, handler) {
3759d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee	if (element.addEventListener) {
3769d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee		element.addEventListener(type, handler, false);
3779d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee	} else {
3789d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee		// assign each event handler a unique ID
3799d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee		if (!handler.$$guid) handler.$$guid = dean_addEvent.guid++;
3809d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee		// create a hash table of event types for the element
3819d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee		if (!element.events) element.events = {};
3829d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee		// create a hash table of event handlers for each element/event pair
3839d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee		var handlers = element.events[type];
3849d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee		if (!handlers) {
3859d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee			handlers = element.events[type] = {};
3869d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee			// store the existing event handler (if there is one)
3879d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee			if (element["on" + type]) {
3889d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee				handlers[0] = element["on" + type];
3899d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee			}
3909d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee		}
3919d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee		// store the event handler in the hash table
3929d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee		handlers[handler.$$guid] = handler;
3939d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee		// assign a global event handler to do all the work
3949d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee		element["on" + type] = handleEvent;
3959d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee	}
3969d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee};
3979d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee// a counter used to create unique IDs
3989d2d6e14b0932b6a74e01f393d5efed61458941bBob Leedean_addEvent.guid = 1;
3999d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee
4009d2d6e14b0932b6a74e01f393d5efed61458941bBob Leefunction removeEvent(element, type, handler) {
4019d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee	if (element.removeEventListener) {
4029d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee		element.removeEventListener(type, handler, false);
4039d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee	} else {
4049d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee		// delete the event handler from the hash table
4059d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee		if (element.events && element.events[type]) {
4069d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee			delete element.events[type][handler.$$guid];
4079d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee		}
4089d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee	}
4099d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee};
4109d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee
4119d2d6e14b0932b6a74e01f393d5efed61458941bBob Leefunction handleEvent(event) {
4129d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee	var returnValue = true;
4139d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee	// grab the event object (IE uses a global event object)
4149d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee	event = event || fixEvent(((this.ownerDocument || this.document || this).parentWindow || window).event);
4159d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee	// get a reference to the hash table of event handlers
4169d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee	var handlers = this.events[event.type];
4179d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee	// execute each event handler
4189d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee	for (var i in handlers) {
4199d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee		this.$$handleEvent = handlers[i];
4209d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee		if (this.$$handleEvent(event) === false) {
4219d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee			returnValue = false;
4229d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee		}
4239d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee	}
4249d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee	return returnValue;
4259d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee};
4269d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee
4279d2d6e14b0932b6a74e01f393d5efed61458941bBob Leefunction fixEvent(event) {
4289d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee	// add W3C standard event methods
4299d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee	event.preventDefault = fixEvent.preventDefault;
4309d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee	event.stopPropagation = fixEvent.stopPropagation;
4319d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee	return event;
4329d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee};
4339d2d6e14b0932b6a74e01f393d5efed61458941bBob LeefixEvent.preventDefault = function() {
4349d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee	this.returnValue = false;
4359d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee};
4369d2d6e14b0932b6a74e01f393d5efed61458941bBob LeefixEvent.stopPropagation = function() {
4379d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee  this.cancelBubble = true;
4389d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee}
4399d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee
4409d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee// Dean's forEach: http://dean.edwards.name/base/forEach.js
4419d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee/*
4429d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee	forEach, version 1.0
4439d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee	Copyright 2006, Dean Edwards
4449d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee	License: http://www.opensource.org/licenses/mit-license.php
4459d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee*/
4469d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee
4479d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee// array-like enumeration
4489d2d6e14b0932b6a74e01f393d5efed61458941bBob Leeif (!Array.forEach) { // mozilla already supports this
4499d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee	Array.forEach = function(array, block, context) {
4509d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee		for (var i = 0; i < array.length; i++) {
4519d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee			block.call(context, array[i], i, array);
4529d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee		}
4539d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee	};
4549d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee}
4559d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee
4569d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee// generic enumeration
4579d2d6e14b0932b6a74e01f393d5efed61458941bBob LeeFunction.prototype.forEach = function(object, block, context) {
4589d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee	for (var key in object) {
4599d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee		if (typeof this.prototype[key] == "undefined") {
4609d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee			block.call(context, object[key], key, object);
4619d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee		}
4629d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee	}
4639d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee};
4649d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee
4659d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee// character enumeration
4669d2d6e14b0932b6a74e01f393d5efed61458941bBob LeeString.forEach = function(string, block, context) {
4679d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee	Array.forEach(string.split(""), function(chr, index) {
4689d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee		block.call(context, chr, index, string);
4699d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee	});
4709d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee};
4719d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee
4729d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee// globally resolve forEach enumeration
4739d2d6e14b0932b6a74e01f393d5efed61458941bBob Leevar forEach = function(object, block, context) {
4749d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee	if (object) {
4759d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee		var resolve = Object; // default
4769d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee		if (object instanceof Function) {
4779d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee			// functions have a "length" property
4789d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee			resolve = Function;
4799d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee		} else if (object.forEach instanceof Function) {
4809d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee			// the object implements a custom forEach method so use that
4819d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee			object.forEach(block, context);
4829d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee			return;
4839d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee		} else if (typeof object == "string") {
4849d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee			// the object is a string
4859d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee			resolve = String;
4869d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee		} else if (typeof object.length == "number") {
4879d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee			// the object is array-like
4889d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee			resolve = Array;
4899d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee		}
4909d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee		resolve.forEach(object, block, context);
4919d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee	}
4929d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee};
4939d2d6e14b0932b6a74e01f393d5efed61458941bBob Lee
494