]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | SortTable | |
3 | version 2 | |
4 | 7th April 2007 | |
5 | Stuart Langridge, http://www.kryogenix.org/code/browser/sorttable/ | |
6 | ||
7 | Instructions: | |
8 | Download this file | |
9 | Add <script src="sorttable.js"></script> to your HTML | |
10 | Add class="sortable" to any table you'd like to make sortable | |
11 | Click on the headers to sort | |
12 | ||
13 | Thanks to many, many people for contributions and suggestions. | |
14 | Licenced as X11: http://www.kryogenix.org/code/browser/licence.html | |
15 | This basically means: do what you want with it. | |
16 | */ | |
17 | ||
18 | ||
19 | var stIsIE = /*@cc_on!@*/false; | |
20 | ||
21 | sorttable = { | |
22 | init: function() { | |
23 | // quit if this function has already been called | |
24 | if (arguments.callee.done) return; | |
25 | // flag this function so we don't do the same thing twice | |
26 | arguments.callee.done = true; | |
27 | // kill the timer | |
28 | if (_timer) clearInterval(_timer); | |
29 | ||
30 | if (!document.createElement || !document.getElementsByTagName) return; | |
31 | ||
32 | sorttable.DATE_RE = /^(\d\d?)[\/\.-](\d\d?)[\/\.-]((\d\d)?\d\d)$/; | |
33 | ||
34 | forEach(document.getElementsByTagName('table'), function(table) { | |
35 | if (table.className.search(/\bsortable\b/) != -1) { | |
36 | sorttable.makeSortable(table); | |
37 | } | |
38 | }); | |
39 | ||
40 | }, | |
41 | ||
42 | makeSortable: function(table) { | |
43 | if (table.getElementsByTagName('thead').length == 0) { | |
44 | // table doesn't have a tHead. Since it should have, create one and | |
45 | // put the first table row in it. | |
46 | the = document.createElement('thead'); | |
47 | the.appendChild(table.rows[0]); | |
48 | table.insertBefore(the,table.firstChild); | |
49 | } | |
50 | // Safari doesn't support table.tHead, sigh | |
51 | if (table.tHead == null) table.tHead = table.getElementsByTagName('thead')[0]; | |
52 | ||
53 | if (table.tHead.rows.length != 1) return; // can't cope with two header rows | |
54 | ||
55 | // Sorttable v1 put rows with a class of "sortbottom" at the bottom (as | |
56 | // "total" rows, for example). This is B&R, since what you're supposed | |
57 | // to do is put them in a tfoot. So, if there are sortbottom rows, | |
58 | // for backwards compatibility, move them to tfoot (creating it if needed). | |
59 | sortbottomrows = []; | |
60 | for (var i=0; i<table.rows.length; i++) { | |
61 | if (table.rows[i].className.search(/\bsortbottom\b/) != -1) { | |
62 | sortbottomrows[sortbottomrows.length] = table.rows[i]; | |
63 | } | |
64 | } | |
65 | if (sortbottomrows) { | |
66 | if (table.tFoot == null) { | |
67 | // table doesn't have a tfoot. Create one. | |
68 | tfo = document.createElement('tfoot'); | |
69 | table.appendChild(tfo); | |
70 | } | |
71 | for (var i=0; i<sortbottomrows.length; i++) { | |
72 | tfo.appendChild(sortbottomrows[i]); | |
73 | } | |
74 | delete sortbottomrows; | |
75 | } | |
76 | ||
77 | // work through each column and calculate its type | |
78 | headrow = table.tHead.rows[0].cells; | |
79 | for (var i=0; i<headrow.length; i++) { | |
80 | // manually override the type with a sorttable_type attribute | |
81 | if (!headrow[i].className.match(/\bsorttable_nosort\b/)) { // skip this col | |
82 | mtch = headrow[i].className.match(/\bsorttable_([a-z0-9]+)\b/); | |
83 | if (mtch) { override = mtch[1]; } | |
84 | if (mtch && typeof sorttable["sort_"+override] == 'function') { | |
85 | headrow[i].sorttable_sortfunction = sorttable["sort_"+override]; | |
86 | } else { | |
87 | headrow[i].sorttable_sortfunction = sorttable.guessType(table,i); | |
88 | } | |
89 | // make it clickable to sort | |
90 | headrow[i].sorttable_columnindex = i; | |
91 | headrow[i].sorttable_tbody = table.tBodies[0]; | |
92 | dean_addEvent(headrow[i],"click", sorttable.innerSortFunction = function(e) { | |
93 | ||
94 | if (this.className.search(/\bsorttable_sorted\b/) != -1) { | |
95 | // if we're already sorted by this column, just | |
96 | // reverse the table, which is quicker | |
97 | sorttable.reverse(this.sorttable_tbody); | |
98 | this.className = this.className.replace('sorttable_sorted', | |
99 | 'sorttable_sorted_reverse'); | |
100 | this.removeChild(document.getElementById('sorttable_sortfwdind')); | |
101 | sortrevind = document.createElement('span'); | |
102 | sortrevind.id = "sorttable_sortrevind"; | |
103 | sortrevind.innerHTML = stIsIE ? ' <font face="webdings">5</font>' : ' ▴'; | |
104 | this.appendChild(sortrevind); | |
105 | return; | |
106 | } | |
107 | if (this.className.search(/\bsorttable_sorted_reverse\b/) != -1) { | |
108 | // if we're already sorted by this column in reverse, just | |
109 | // re-reverse the table, which is quicker | |
110 | sorttable.reverse(this.sorttable_tbody); | |
111 | this.className = this.className.replace('sorttable_sorted_reverse', | |
112 | 'sorttable_sorted'); | |
113 | this.removeChild(document.getElementById('sorttable_sortrevind')); | |
114 | sortfwdind = document.createElement('span'); | |
115 | sortfwdind.id = "sorttable_sortfwdind"; | |
116 | sortfwdind.innerHTML = stIsIE ? ' <font face="webdings">6</font>' : ' ▾'; | |
117 | this.appendChild(sortfwdind); | |
118 | return; | |
119 | } | |
120 | ||
121 | // remove sorttable_sorted classes | |
122 | theadrow = this.parentNode; | |
123 | forEach(theadrow.childNodes, function(cell) { | |
124 | if (cell.nodeType == 1) { // an element | |
125 | cell.className = cell.className.replace('sorttable_sorted_reverse',''); | |
126 | cell.className = cell.className.replace('sorttable_sorted',''); | |
127 | } | |
128 | }); | |
129 | sortfwdind = document.getElementById('sorttable_sortfwdind'); | |
130 | if (sortfwdind) { sortfwdind.parentNode.removeChild(sortfwdind); } | |
131 | sortrevind = document.getElementById('sorttable_sortrevind'); | |
132 | if (sortrevind) { sortrevind.parentNode.removeChild(sortrevind); } | |
133 | ||
134 | this.className += ' sorttable_sorted'; | |
135 | sortfwdind = document.createElement('span'); | |
136 | sortfwdind.id = "sorttable_sortfwdind"; | |
137 | sortfwdind.innerHTML = stIsIE ? ' <font face="webdings">6</font>' : ' ▾'; | |
138 | this.appendChild(sortfwdind); | |
139 | ||
140 | // build an array to sort. This is a Schwartzian transform thing, | |
141 | // i.e., we "decorate" each row with the actual sort key, | |
142 | // sort based on the sort keys, and then put the rows back in order | |
143 | // which is a lot faster because you only do getInnerText once per row | |
144 | row_array = []; | |
145 | col = this.sorttable_columnindex; | |
146 | rows = this.sorttable_tbody.rows; | |
147 | for (var j=0; j<rows.length; j++) { | |
148 | row_array[row_array.length] = [sorttable.getInnerText(rows[j].cells[col]), rows[j]]; | |
149 | } | |
150 | /* If you want a stable sort, uncomment the following line */ | |
151 | //sorttable.shaker_sort(row_array, this.sorttable_sortfunction); | |
152 | /* and comment out this one */ | |
153 | row_array.sort(this.sorttable_sortfunction); | |
154 | ||
155 | tb = this.sorttable_tbody; | |
156 | for (var j=0; j<row_array.length; j++) { | |
157 | tb.appendChild(row_array[j][1]); | |
158 | } | |
159 | ||
160 | delete row_array; | |
161 | }); | |
162 | } | |
163 | } | |
164 | }, | |
165 | ||
166 | guessType: function(table, column) { | |
167 | // guess the type of a column based on its first non-blank row | |
168 | sortfn = sorttable.sort_alpha; | |
169 | for (var i=0; i<table.tBodies[0].rows.length; i++) { | |
170 | text = sorttable.getInnerText(table.tBodies[0].rows[i].cells[column]); | |
171 | if (text != '') { | |
172 |