2 * Table sorting script by Joost de Valk, check it out at http://www.joostdevalk.nl/code/sortable-table/.
3 * Based on a script from http://www.kryogenix.org/code/browser/sorttable/.
4 * Distributed under the MIT license: http://www.kryogenix.org/code/browser/licence.html .
6 * Copyright (c) 1997-2006 Stuart Langridge, Joost de Valk.
8 * @todo don't break on colspans/rowspans (bug 8028)
9 * @todo language-specific digit grouping/decimals (bug 8063)
10 * @todo support all accepted date formats (bug 8226)
13 var image_path
= stylepath
+"/common/images/";
14 var image_up
= "sort_up.gif";
15 var image_down
= "sort_down.gif";
16 var image_none
= "sort_none.gif";
17 var europeandate
= wgContentLanguage
!= "en"; // The non-American-inclined can change to "true"
19 var alternate_row_colors
= true;
22 hookEvent( "load", sortables_init
);
24 var SORT_COLUMN_INDEX
;
27 function sortables_init() {
29 // Find all tables with class sortable and make them sortable
30 if (!document
.getElementsByTagName
) return;
31 tbls
= document
.getElementsByTagName("table");
32 for (ti
=0;ti
<tbls
.length
;ti
++) {
34 if ( (' '+thisTbl
.className
+' ').indexOf("sortable") != -1 ) {
36 thisTbl
.setAttribute('id','sortable_table_id_'+idnum
);
39 ts_makeSortable(thisTbl
);
44 function ts_makeSortable(table
) {
45 if (table
.rows
&& table
.rows
.length
> 0) {
46 if (table
.tHead
&& table
.tHead
.rows
.length
> 0) {
47 var firstRow
= table
.tHead
.rows
[table
.tHead
.rows
.length
-1];
50 var firstRow
= table
.rows
[0];
53 if (!firstRow
) return;
55 // We have a first row: assume it's the header, and make its contents clickable links
56 for (var i
=0;i
<firstRow
.cells
.length
;i
++) {
57 var cell
= firstRow
.cells
[i
];
58 var txt
= ts_getInnerText(cell
);
59 if (cell
.className
!= "unsortable" && cell
.className
.indexOf("unsortable") == -1) {
60 cell
.innerHTML
= txt
+' <a href="#" class="sortheader" onclick="ts_resortTable(this);return false;"><span class="sortarrow"><img src="'+ image_path
+ image_none
+ '" alt="↓"/></span></a>';
63 if (alternate_row_colors
) {
68 function ts_getInnerText(el
) {
69 if (typeof el
== "string") return el
;
70 if (typeof el
== "undefined") { return el
};
71 if (el
.innerText
) return el
.innerText
; //Not needed but it is faster
74 var cs
= el
.childNodes
;
76 for (var i
= 0; i
< l
; i
++) {
77 switch (cs
[i
].nodeType
) {
78 case 1: //ELEMENT_NODE
79 str
+= ts_getInnerText(cs
[i
]);
82 str
+= cs
[i
].nodeValue
;
89 function ts_resortTable(lnk
) {
92 for (var ci
=0;ci
<lnk
.childNodes
.length
;ci
++) {
93 if (lnk
.childNodes
[ci
].tagName
&& lnk
.childNodes
[ci
].tagName
.toLowerCase() == 'span') span
= lnk
.childNodes
[ci
];
95 var spantext
= ts_getInnerText(span
);
96 var td
= lnk
.parentNode
;
97 var column
= td
.cellIndex
;
98 var table
= getParent(td
,'TABLE');
100 // Work out a type for the column
101 if (table
.rows
.length
<= 1) return;
103 for( var i
= 1, itm
= ""; itm
.match(/^([\s]|\n|\ |<!--[^-]+-->)*$/); i
++) {
104 var itm
= ts_getInnerText(table
.tBodies
[0].rows
[i
].cells
[column
]);
107 sortfn
= ts_sort_caseinsensitive
;
108 if (itm
.match(/^\d\d[\/. -][a-zA-Z]{3}[\/. -]\d\d\d\d$/)) sortfn
= ts_sort_date
;
109 if (itm
.match(/^\d\d[\/.-]\d\d[\/.-]\d\d\d\d$/)) sortfn
= ts_sort_date
;
110 if (itm
.match(/^\d\d[\/.-]\d\d[\/.-]\d\d$/)) sortfn
= ts_sort_date
;
111 if (itm
.match(/^[£$\80Û¢´]/)) sortfn
= ts_sort_currency
;
112 if (itm
.match(/^[\d.,]+\%?$/)) sortfn
= ts_sort_numeric
;
113 SORT_COLUMN_INDEX
= column
;
114 var firstRow
= new Array();
115 var newRows
= new Array();
117 for (k
=0;k
<table
.tBodies
.length
;k
++) {
118 for (i
=0;i
<table
.tBodies
[k
].rows
[0].length
;i
++) {
119 firstRow
[i
] = table
.tBodies
[k
].rows
[0][i
];
123 for (k
=0;k
<table
.tBodies
.length
;k
++) {
125 // Skip the first row
126 for (j
=1;j
<table
.tBodies
[k
].rows
.length
;j
++) {
127 newRows
[j
-1] = table
.tBodies
[k
].rows
[j
];
130 // Do NOT skip the first row
131 for (j
=0;j
<table
.tBodies
[k
].rows
.length
;j
++) {
132 newRows
[j
] = table
.tBodies
[k
].rows
[j
];
137 newRows
.sort(sortfn
);
139 if (span
.getAttribute("sortdir") == 'down') {
140 ARROW
= '<img src="'+ image_path
+ image_down
+ '" alt="↓"/>';
142 span
.setAttribute('sortdir','up');
144 ARROW
= '<img src="'+ image_path
+ image_up
+ '" alt="↑"/>';
145 span
.setAttribute('sortdir','down');
148 // We appendChild rows that already exist to the tbody, so it moves them rather than creating new ones
149 // don't do sortbottom rows
150 for (i
=0; i
<newRows
.length
; i
++) {
151 if (!newRows
[i
].className
|| (newRows
[i
].className
&& (newRows
[i
].className
.indexOf('sortbottom') == -1))) {
152 table
.tBodies
[0].appendChild(newRows
[i
]);
155 // do sortbottom rows only
156 for (i
=0; i
<newRows
.length
; i
++) {
157 if (newRows
[i
].className
&& (newRows
[i
].className
.indexOf('sortbottom') != -1))
158 table
.tBodies
[0].appendChild(newRows
[i
]);
161 // Delete any other arrows there may be showing
162 var allspans
= document
.getElementsByTagName("span");
163 for (var ci
=0;ci
<allspans
.length
;ci
++) {
164 if (allspans
[ci
].className
== 'sortarrow') {
165 if (getParent(allspans
[ci
],"table") == getParent(lnk
,"table")) { // in the same table as us?
166 allspans
[ci
].innerHTML
= '<img src="'+ image_path
+ image_none
+ '" alt="↓"/>';
171 span
.innerHTML
= ARROW
;
175 function getParent(el
, pTagName
) {
178 } else if (el
.nodeType
== 1 && el
.tagName
.toLowerCase() == pTagName
.toLowerCase()) { // Gecko bug, supposed to be uppercase
181 return getParent(el
.parentNode
, pTagName
);
185 function sort_date(date
) {
186 // y2k notes: two digit years less than 50 are treated as 20XX, greater than 50 are treated as 19XX
188 if (date
.length
== 11) {
189 monthstr
= date
.substr(3,3);
190 monthstr
= monthstr
.toLowerCase();
192 case "jan": var month
= "01"; break;
193 case "feb": var month
= "02"; break;
194 case "mar": var month
= "03"; break;
195 case "apr": var month
= "04"; break;
196 case "may": var month
= "05"; break;
197 case "jun": var month
= "06"; break;
198 case "jul": var month
= "07"; break;
199 case "aug": var month
= "08"; break;
200 case "sep": var month
= "09"; break;
201 case "oct": var month
= "10"; break;
202 case "nov": var month
= "11"; break;
203 case "dec": var month
= "12"; break;
204 // default: var month = "00";
206 dt
= date
.substr(7,4)+month
+date
.substr(0,2);
208 } else if (date
.length
== 10) {
209 if (europeandate
== false) {
210 dt
= date
.substr(6,4)+date
.substr(0,2)+date
.substr(3,2);
213 dt
= date
.substr(6,4)+date
.substr(3,2)+date
.substr(0,2);
216 } else if (date
.length
== 8) {
217 yr
= date
.substr(6,2);
218 if (parseInt(yr
) < 50) {
223 if (europeandate
== true) {
224 dt
= yr
+date
.substr(3,2)+date
.substr(0,2);
227 dt
= yr
+date
.substr(0,2)+date
.substr(3,2);
234 function ts_sort_date(a
,b
) {
235 dt1
= sort_date(ts_getInnerText(a
.cells
[SORT_COLUMN_INDEX
]));
236 dt2
= sort_date(ts_getInnerText(b
.cells
[SORT_COLUMN_INDEX
]));
247 function ts_sort_currency(a
,b
) {
248 aa
= ts_getInnerText(a
.cells
[SORT_COLUMN_INDEX
]).replace(/[^0-9.]/g,'');
249 bb
= ts_getInnerText(b
.cells
[SORT_COLUMN_INDEX
]).replace(/[^0-9.]/g,'');
250 return compare_numeric(aa
,bb
);
253 function ts_sort_numeric(a
,b
) {
254 aa
= ts_getInnerText(a
.cells
[SORT_COLUMN_INDEX
]);
255 bb
= ts_getInnerText(b
.cells
[SORT_COLUMN_INDEX
]);
256 return compare_numeric(aa
,bb
);
259 function compare_numeric(a
,b
) {
260 a
= parseFloat(a
.replace(/,/, ""));
261 a
= (isNaN(a
) ? 0 : a
);
262 b
= parseFloat(b
.replace(/,/, ""));
263 b
= (isNaN(b
) ? 0 : b
);
267 function ts_sort_caseinsensitive(a
,b
) {
268 aa
= ts_getInnerText(a
.cells
[SORT_COLUMN_INDEX
]).toLowerCase();
269 bb
= ts_getInnerText(b
.cells
[SORT_COLUMN_INDEX
]).toLowerCase();
279 function ts_sort_default(a
,b
) {
280 aa
= ts_getInnerText(a
.cells
[SORT_COLUMN_INDEX
]);
281 bb
= ts_getInnerText(b
.cells
[SORT_COLUMN_INDEX
]);
291 function addEvent(elm
, evType
, fn
, useCapture
)
292 // addEvent and removeEvent
293 // cross-browser event handling for IE5+, NS6 and Mozilla
296 if (elm
.addEventListener
){
297 elm
.addEventListener(evType
, fn
, useCapture
);
299 } else if (elm
.attachEvent
){
300 var r
= elm
.attachEvent("on"+evType
, fn
);
303 alert("Handler could not be removed");
307 function replace(s
, t
, u
) {
309 ** Replace a token in a string
310 ** s string to be processed
311 ** t token to be found and removed
312 ** u token to be inserted
313 ** returns new String
317 if (i
== -1) return s
;
318 r
+= s
.substring(0,i
) + u
;
319 if ( i
+ t
.length
< s
.length
) {
320 r
+= replace(s
.substring(i
+ t
.length
, s
.length
), t
, u
);
326 return s
.replace(/^([ \t]|\n|\ |<!--[^-]+-->)*/, "").replace(/([ \t]|\n|\ |<!--[^-]+-->)*$/, "");
329 function alternate(table
) {
330 // Take object table and get all it's tbodies.
331 var tableBodies
= table
.getElementsByTagName("tbody");
332 // Loop through these tbodies
333 for (var i
= 0; i
< tableBodies
.length
; i
++) {
334 // Take the tbody, and get all it's rows
335 var tableRows
= tableBodies
[i
].getElementsByTagName("tr");
336 // Loop through these rows
337 // Start at 1 because we want to leave the heading row untouched
338 for (var j
= 0; j
< tableRows
.length
; j
++) {
339 // Check if j is even, and apply classes for both possible results
340 if ( (j
% 2) == 0 ) {
341 if ( !(tableRows
[j
].className
.indexOf('odd') == -1) ) {
342 tableRows
[j
].className
= replace(tableRows
[j
].className
, 'odd', 'even');
344 if ( tableRows
[j
].className
.indexOf('even') == -1 ) {
345 tableRows
[j
].className
+= " even";
349 if ( !(tableRows
[j
].className
.indexOf('even') == -1) ) {
350 tableRows
[j
].className
= replace(tableRows
[j
].className
, 'even', 'odd');
352 if ( tableRows
[j
].className
.indexOf('odd') == -1 ) {
353 tableRows
[j
].className
+= " odd";