Setting tag plewww-5.2-4
[plewww.git] / plekit / tablesort / tablesort.js
1 /*
2         TableSort revisited v5.0 by frequency-decoder.com
3
4         Released under a creative commons Attribution-ShareAlike 2.5 license (http://creativecommons.org/licenses/by-sa/2.5/)
5
6         Please credit frequency decoder in any derivative work - thanks
7
8         You are free:
9
10         * to copy, distribute, display, and perform the work
11         * to make derivative works
12         * to make commercial use of the work
13
14         Under the following conditions:
15
16                 by Attribution.
17                 --------------
18                 You must attribute the work in the manner specified by the author or licensor.
19
20                 sa
21                 --
22                 Share Alike. If you alter, transform, or build upon this work, you may distribute the resulting work only under a license identical to this one.
23
24         * For any reuse or distribution, you must make clear to others the license terms of this work.
25         * Any of these conditions can be waived if you get permission from the copyright holder.
26 */
27
28 var debug=false;
29
30 (function() {
31 fdTableSort = {
32         regExp_Currency:        /^[£$€¥¤]/,
33         regExp_Number:          /^(\-)?[0-9]+(\.[0-9]*)?$/,
34         pos:                    -1,
35         uniqueHash:             1,
36         thNode:                 null,
37         tableId:                null,
38         tableCache:             {},
39         tmpCache:               {},
40         sortActiveClass:        "sort-active",
41         /*@cc_on
42         /*@if (@_win32)
43         colspan:                "colSpan",
44         rowspan:                "rowSpan",
45         @else @*/
46         colspan:                "colspan",
47         rowspan:                "rowspan",
48         /*@end
49         @*/
50         
51         addEvent: function(obj, type, fn, tmp) {
52                 tmp || (tmp = true);
53                 if( obj.attachEvent ) {
54                         obj["e"+type+fn] = fn;
55                         obj[type+fn] = function(){obj["e"+type+fn]( window.event );};
56                         obj.attachEvent( "on"+type, obj[type+fn] );
57                 } else {
58                         obj.addEventListener( type, fn, true );
59                 };
60         },
61         removeEvent: function(obj, type, fn, tmp) {
62                 tmp || (tmp = true);
63                 try {
64                         if( obj.detachEvent ) {
65                                 obj.detachEvent( "on"+type, obj[type+fn] );
66                                 obj[type+fn] = null;
67                         } else {
68                                 obj.removeEventListener( type, fn, true );
69                         };
70                 } catch(err) {};
71         },
72         stopEvent: function(e) {
73                 e = e || window.event;
74
75                 if(e.stopPropagation) {
76                         e.stopPropagation();
77                         e.preventDefault();
78                 };
79                 
80                 /*@cc_on@*/
81                 /*@if(@_win32)
82                 e.cancelBubble = true;
83                 e.returnValue  = false;
84                 /*@end@*/
85                 return false;
86         },
87         parseClassName: function(head, tbl) {
88                 var colMatch = tbl.className.match(new RegExp(head + "((-[\\d]+([r]){0,1})+)"));
89                 return colMatch && colMatch.length ? colMatch[0].replace(head, "").split("-") : [];
90         },
91         disableSelection: function(element) {
92                 element.onselectstart = function() {
93                         return false;
94                 };
95                 element.unselectable = "on";
96                 element.style.MozUserSelect = "none";
97         },
98         removeTableCache: function(tableId) {
99                 if(!(tableId in fdTableSort.tableCache)) return;
100
101                 fdTableSort.tableCache[tableId] = null;
102                 delete fdTableSort.tableCache[tableId];
103
104                 var tbl = document.getElementById(tableId);
105                 if(!tbl) return;
106                 var ths = tbl.getElementsByTagName("th");
107                 var a;
108                 for(var i = 0, th; th = ths[i]; i++) {
109                         a = th.getElementsByTagName("a");
110                         if(a.length) a[0].onkeydown = a[0].onclick = null;
111                         th.onclick = th.onselectstart = th = a = null;
112                 };
113         },
114         removeTmpCache: function(tableId) {
115                 if(!(tableId in fdTableSort.tmpCache)) return;
116                 var headers = fdTableSort.tmpCache[tableId].headers;
117                 var a;
118                 for(var i = 0, row; row = headers[i]; i++) {
119                         for(var j = 0, th; th = row[j]; j++) {
120                                 a = th.getElementsByTagName("a");
121                                 if(a.length) a[0].onkeydown = a[0].onclick = null;
122                                 th.onclick = th.onselectstart = th = a = null;
123                         };
124                 };
125                 fdTableSort.tmpCache[tableId] = null;
126                 delete fdTableSort.tmpCache[tableId];
127         },
128         initEvt: function(e) {
129                 fdTableSort.init(false);
130         },
131         init: function(tableId) {
132                 if (debug) plc_message ('entering tablesort.init');
133                 if (!document.getElementsByTagName || !document.createElement || !document.getElementById) return;
134
135                 var tables = tableId && document.getElementById(tableId) ? [document.getElementById(tableId)] : document.getElementsByTagName("table");
136                 var c, ii, len, colMatch, showOnly, match, showArrow, columnNumSortObj, obj, workArr, headers, thtext, aclone, multi, colCnt, cel, allRowArr, rowArr, sortableTable, celCount, colspan, rowspan, rowLength;
137
138                 var a          = document.createElement("a");
139                 a.href         = "#";
140                 a.className    = "fdTableSortTrigger";
141
142                 var span       = document.createElement("span");
143
144                 for(var k = 0, tbl; tbl = tables[k]; k++) {
145
146                         if(tbl.id) {
147                                 fdTableSort.removeTableCache(tbl.id);
148                                 fdTableSort.removeTmpCache(tbl.id);
149                         };
150
151                         allRowArr     = tbl.getElementsByTagName('thead').length ? tbl.getElementsByTagName('thead')[0].getElementsByTagName('tr') : tbl.getElementsByTagName('tr');
152                         rowArr        = [];
153                         sortableTable = false;
154
155                         for(var i = 0, tr; tr = allRowArr[i]; i++) {
156                                 if(tr.getElementsByTagName('td').length || !tr.getElementsByTagName('th').length) { continue; };
157                                 rowArr[rowArr.length] = tr.getElementsByTagName('th');
158                                 for(var j = 0, th; th = rowArr[rowArr.length - 1][j]; j++) {
159                                         if(th.className.search(/sortable/) != -1) { sortableTable = true; };
160                                 };
161                         };
162
163                         if(!sortableTable) continue;
164
165                         if(!tbl.id) { tbl.id = "fd-table-" + fdTableSort.uniqueHash++; };
166
167                         showArrow   = tbl.className.search("no-arrow") == -1;
168                         showOnly    = tbl.className.search("sortable-onload-show") != -1;
169
170                         columnNumSortObj = {};
171                         colMatch         = fdTableSort.parseClassName(showOnly ? "sortable-onload-show" : "sortable-onload", tbl);
172                         for(match = 1; match < colMatch.length; match++) {
173                                 columnNumSortObj[parseInt(colMatch[match], 10)] = { "reverse":colMatch[match].search("r") != -1 };
174                         };
175
176                         rowLength = rowArr[0].length;
177
178                         for(c = 0;c < rowArr[0].length;c++){
179                                 if(rowArr[0][c].getAttribute(fdTableSort.colspan) && rowArr[0][c].getAttribute(fdTableSort.colspan) > 1){
180                                         rowLength = rowLength + (rowArr[0][c].getAttribute(fdTableSort.colspan) - 1);
181                                 };
182                         };
183
184                         workArr = new Array(rowArr.length);
185                         for(c = rowArr.length;c--;){ workArr[c]= new Array(rowLength); };
186
187                         for(c = 0;c < workArr.length;c++){
188                                 celCount = 0;
189                                 for(i = 0;i < rowLength;i++){
190                                         if(!workArr[c][i]){
191                                                 cel = rowArr[c][celCount];
192                                                 colspan = (cel.getAttribute(fdTableSort.colspan) > 1) ? cel.getAttribute(fdTableSort.colspan):1;
193                                                 rowspan = (cel.getAttribute(fdTableSort.rowspan) > 1) ? cel.getAttribute(fdTableSort.rowspan):1;
194                                                 for(var t = 0;((t < colspan)&&((i+t) < rowLength));t++){
195                                                         for(var n = 0;((n < rowspan)&&((c+n) < workArr.length));n++) {
196                                                                 workArr[(c+n)][(i+t)] = cel;
197                                                         };
198                                                 };
199                                                 if(++celCount == rowArr[c].length) break;
200                                         };
201                                 };
202                         };
203
204                         for(c = 0;c < workArr.length;c++) {
205                                 for(i = 0;i < workArr[c].length;i++){
206
207                                         if(workArr[c][i].className.search("fd-column-") == -1 && workArr[c][i].className.search("sortable") != -1) workArr[c][i].className = workArr[c][i].className + " fd-column-" + i;
208
209                                         if(workArr[c][i].className.match('sortable')) {
210                                                 workArr[c][i].className = workArr[c][i].className.replace(/forwardSort|reverseSort/, "");
211
212                                                 if(i in columnNumSortObj) {
213                                                         columnNumSortObj[i]["thNode"] = workArr[c][i];
214                                                         columnNumSortObj["active"] = true;
215                                                 };
216                                                 th_elt = workArr[c][i];
217                                                 thtext = fdTableSort.getInnerText(th_elt, true);
218                                                 
219                                                 for(var cn = workArr[c][i].childNodes.length; cn--;) {
220                                                         // Skip image nodes and links created by the filter script.
221                                                         if(workArr[c][i].childNodes[cn].nodeType == 1 && (workArr[c][i].childNodes[cn].className == "fdFilterTrigger" || /img/i.test(workArr[c][i].childNodes[cn].nodeName))) {
222                                                                 continue;
223                                                         };
224                                                         if(workArr[c][i].childNodes[cn].nodeType == 1 && /^a$/i.test(workArr[c][i].childNodes[cn].nodeName)) {
225                                                                 workArr[c][i].childNodes[cn].onclick = workArr[c][i].childNodes[cn].onkeydown = null;
226                                                         };
227                                                         workArr[c][i].removeChild(workArr[c][i].childNodes[cn]);
228                                                 };
229
230                                                 aclone = a.cloneNode(true);
231                                                 //aclone.appendChild(document.createTextNode(thtext));
232                                                 aclone.innerHTML = thtext;
233                                                 //if the <th> has title set, use this as an alternate text for the 'sort on ...'
234                                                 if (th_elt.title) {
235                                                   aclone.title = th_elt.title;
236                                                 } else {
237                                                   aclone.title = "Sort on \u201c" + (th_elt.title ? th_elt.title : thtext.replace('<br />', '')) + "\u201d";
238                                                 }
239                                                 aclone.onclick = aclone.onkeydown = workArr[c][i].onclick = fdTableSort.initWrapper;
240                                                 workArr[c][i].appendChild(aclone);
241                                                 if(showArrow) workArr[c][i].appendChild(span.cloneNode(false));
242                                                 workArr[c][i].className = workArr[c][i].className.replace(/fd-identical|fd-not-identical/, "");
243                                                 fdTableSort.disableSelection(workArr[c][i]);
244                                                 aclone = null;
245                                         };
246                                 };
247                         };
248
249                         fdTableSort.tmpCache[tbl.id] = {cols:rowLength, headers:workArr};
250
251                         workArr = null;
252                         multi   = 0;
253
254                         if("active" in columnNumSortObj) {
255                                 fdTableSort.tableId = tbl.id;
256                                 fdTableSort.prepareTableData(document.getElementById(fdTableSort.tableId));
257
258                                 delete columnNumSortObj["active"];
259
260                                 for(col in columnNumSortObj) {
261                                         obj = columnNumSortObj[col];
262                                         if(!("thNode" in obj)) { continue; };
263                                         fdTableSort.multi = true;
264
265                                         len = obj.reverse ? 2 : 1;
266
267                                         for(ii = 0; ii < len; ii++) {
268                                                 fdTableSort.thNode = obj.thNode;
269                                                 if(!showOnly) {
270                                                         fdTableSort.initSort(false, true);
271                                                 } else {
272                                                         fdTableSort.addThNode();
273                                                 };
274                                         };
275
276                                         if(showOnly) {
277                                                 fdTableSort.removeClass(obj.thNode, "(forwardSort|reverseSort)");
278                                                 fdTableSort.addClass(obj.thNode, obj.reverse ? "reverseSort" : "forwardSort");
279                                                 if(showArrow) {
280                                                         span = fdTableSort.thNode.getElementsByTagName('span')[0];
281                                                         if(span.firstChild) { span.removeChild(span.firstChild); };
282                                                         span.appendChild(document.createTextNode(len == 1 ? " \u2193" : " \u2191"));
283                                                 };
284                                         };
285                                 };
286                                 if(showOnly && (fdTableSort.tableCache[tbl.id].colStyle || fdTableSort.tableCache[tbl.id].rowStyle)) {
287                                         fdTableSort.redraw(tbl.id, false);
288                                 };
289                         } else if(tbl.className.search(/onload-zebra/) != -1) {
290                                 fdTableSort.tableId = tbl.id;
291                                 fdTableSort.prepareTableData(tbl);
292                                 if(fdTableSort.tableCache[tbl.id].rowStyle) { fdTableSort.redraw(tbl.id, false); };
293                         };
294                 };
295
296                 fdTableSort.thNode = aclone = a = span = columnNumSortObj = thNode = tbl = allRowArr = rowArr = null;
297                 if (debug) plc_message ('exiting tablesort.init');
298         },
299         initWrapper: function(e) {
300                 e = e || window.event;
301                 var kc = e.type == "keydown" ? e.keyCode != null ? e.keyCode : e.charCode : -1;
302                 if(fdTableSort.thNode == null && (e.type == "click" || kc == 13)) {
303                         var targ = this;
304                         while(targ.tagName.toLowerCase() != "th") { targ = targ.parentNode; };
305                         fdTableSort.thNode = targ;
306                         while(targ.tagName.toLowerCase() != "table") { targ = targ.parentNode; };
307                         fdTableSort.tableId = targ.id;
308                         fdTableSort.multi = e.shiftKey;
309                         fdTableSort.addSortActiveClass();
310                         setTimeout(fdTableSort.initSort,5,false);
311                         return fdTableSort.stopEvent(e);
312                 };
313                 return kc != -1 ? true : fdTableSort.stopEvent(e);
314         },
315         jsWrapper: function(tableid, colNums) {
316                 if(!(tableid in fdTableSort.tmpCache)) { return false; };
317                 if(!(tableid in fdTableSort.tableCache)) { fdTableSort.prepareTableData(document.getElementById(tableid)); };
318                 if(!(colNums instanceof Array)) { colNums = [colNums]; };
319
320                 fdTableSort.tableId = tableid;
321                 var len = colNums.length, colNum;
322
323                 if(fdTableSort.tableCache[tableid].thList.length == colNums.length) {
324                         var identical = true;
325                         var th;
326                         for(var i = 0; i < len; i++) {
327                                 colNum = colNums[i];
328                                 th = fdTableSort.tmpCache[tableid].headers[0][colNum];
329                                 if(th != fdTableSort.tableCache[tableid].thList[i]) {
330                                         identical = false;
331                                         break;
332                                 };
333                         };
334                         if(identical) {
335                                 fdTableSort.thNode = th;
336                                 fdTableSort.initSort(true);
337                                 return;
338                         };
339                 };
340
341                 fdTableSort.addSortActiveClass();
342
343                 for(var i = 0; i < len; i++) {
344                         fdTableSort.multi = i;
345                         colNum = colNums[i];
346                         fdTableSort.thNode = fdTableSort.tmpCache[tableid].headers[0][colNum];
347                         fdTableSort.initSort(true);
348                 };
349         },
350         addSortActiveClass: function() {
351                 if(fdTableSort.thNode == null) { return; };
352                 fdTableSort.addClass(fdTableSort.thNode, fdTableSort.sortActiveClass);
353                 fdTableSort.addClass(document.getElementsByTagName('body')[0], fdTableSort.sortActiveClass);
354         },
355         removeSortActiveClass: function() {
356                 if(fdTableSort.thNode == null) return;
357                 fdTableSort.removeClass(fdTableSort.thNode, fdTableSort.sortActiveClass);
358                 fdTableSort.removeClass(document.getElementsByTagName('body')[0], fdTableSort.sortActiveClass);
359         },
360         doCallback: function(init) {
361                 if(!fdTableSort.tableId || !(fdTableSort.tableId in fdTableSort.tableCache)) { return; };
362                 fdTableSort.callback(fdTableSort.tableId, init ? fdTableSort.tableCache[fdTableSort.tableId].initiatedCallback : fdTableSort.tableCache[fdTableSort.tableId].completeCallback);
363         },
364         addClass: function(e,c) {
365                 if(new RegExp("(^|\\s)" + c + "(\\s|$)").test(e.className)) { return; };
366                 e.className += ( e.className ? " " : "" ) + c;
367         },
368         /*@cc_on
369         /*@if (@_win32)
370         removeClass: function(e,c) {
371                 e.className = !c ? "" : e.className.replace(new RegExp("(^|\\s)" + c + "(\\s|$)"), " ").replace(/^\s*((?:[\S\s]*\S)?)\s*$/, '$1');
372         },
373         @else @*/
374         removeClass: function(e,c) {
375                 e.className = !c ? "" : e.className.replace(new RegExp("(^|\\s)" + c + "(\\s|$)"), " ").replace(/^\s\s*/, '').replace(/\s\s*$/, '');
376         },
377         /*@end
378         @*/
379         callback: function(tblId, cb) {
380                 var func, parts;
381                 try {
382                         if(cb.indexOf(".") != -1) {                              
383                                 parts = cb.split('.');
384                                 obj   = window;
385                                 for (var x = 0, part; part = obj[parts[x]]; x++) {
386                                         if(part instanceof Function) {
387                                                 (function() {
388                                                         var method = part;
389                                                         func = function (data) { method.apply(obj, [data]) };
390                                                 })();
391                                         } else {
392                                                 obj = part;
393                                         };
394                                 };
395                         } else if(cb + tblId in window) {
396                                 func = window[cb + tblId];
397                         } else if(cb in window) {
398                                 func = window[cb];
399                         } else {
400                                 func = null;
401                         };
402                  } catch(err) {};
403                            
404                 if(!(func instanceof Function)) return;
405                 func(tblId, fdTableSort.tableCache[tblId].thList);                               
406         },
407         prepareTableData: function(table) {
408                 var data = [];
409
410                 var start = table.getElementsByTagName('tbody');
411                 start = start.length ? start[0] : table;
412
413                 var trs = start.rows;
414                 var ths = table.getElementsByTagName('th');
415
416                 var numberOfRows = trs.length;
417                 var numberOfCols = fdTableSort.tmpCache[table.id].cols;
418
419                 var data = [];
420                 var identical = new Array(numberOfCols);
421                 var identVal  = new Array(numberOfCols);
422
423                 for(var tmp = 0; tmp < numberOfCols; tmp++) identical[tmp] = true;
424
425                 var tr, td, th, txt, tds, col, row;
426
427                 var re = new RegExp(/fd-column-([0-9]+)/);
428                 var rowCnt = 0;
429
430                 var sortableColumnNumbers = [];
431
432                 for(var tmp = 0, th; th = ths[tmp]; tmp++) {
433                         if(th.className.search(re) == -1) continue;
434                         sortableColumnNumbers[sortableColumnNumbers.length] = th;
435                 };
436
437                 for(row = 0; row < numberOfRows; row++) {
438
439                         tr              = trs[row];
440                         if(tr.parentNode != start || tr.getElementsByTagName("th").length || (tr.parentNode && tr.parentNode.tagName.toLowerCase().search(/thead|tfoot/) != -1)) continue;
441                         data[rowCnt]    = [];
442                         tds             = tr.cells;
443
444                         for(var tmp = 0, th; th = sortableColumnNumbers[tmp]; tmp++) {
445                                 col = th.className.match(re)[1];
446
447                                 td  = tds[col];
448                                 txt = fdTableSort.getInnerText(td) + " ";
449                                 txt = txt.replace(/^\s+/,'').replace(/\s+$/,'');
450
451                                 if(th.className.search(/sortable-date/) != -1) {
452                                         txt = fdTableSort.dateFormat(txt, th.className.search(/sortable-date-dmy/) != -1);
453                                 } else if(th.className.search(/sortable-numeric|sortable-currency/) != -1) {
454                                         txt = parseFloat(txt.replace(/[^0-9\.\-]/g,''));
455                                         if(isNaN(txt)) txt = "";
456                                 } else if(th.className.search(/sortable-text/) != -1) {
457                                         txt = txt.toLowerCase();
458                                 } else if (th.className.search(/sortable-keep/) != -1) {
459                                         txt = rowCnt;
460                                 } else if(th.className.search(/sortable-([a-zA-Z\_]+)/) != -1) {
461                                         if((th.className.match(/sortable-([a-zA-Z\_]+)/)[1] + "PrepareData") in window) {
462                                                 txt = window[th.className.match(/sortable-([a-zA-Z\_]+)/)[1] + "PrepareData"](td, txt);
463                                         };
464                                 } else if(txt != "") {
465                                         fdTableSort.removeClass(th, "sortable");
466                                         if(fdTableSort.dateFormat(txt) != 0) {
467                                                 fdTableSort.addClass(th, "sortable-date");
468                                                 txt = fdTableSort.dateFormat(txt);
469                                         } else if(txt.search(fdTableSort.regExp_Number) != -1 || txt.search(fdTableSort.regExp_Currency) != -1) {
470                                                 fdTableSort.addClass(th, "sortable-numeric");
471                                                 txt = parseFloat(txt.replace(/[^0-9\.\-]/g,''));
472                                                 if(isNaN(txt)) txt = "";
473                                         } else {
474                                                 fdTableSort.addClass(th, "sortable-text");
475                                                 txt = txt.toLowerCase();
476                                         };
477                                 };
478
479                                 if(rowCnt > 0 && identical[col] && identVal[col] != txt) { identical[col] = false; };
480
481                                 identVal[col]     = txt;
482                                 data[rowCnt][col] = txt;
483                         };
484                         data[rowCnt][numberOfCols] = tr;
485                         rowCnt++;
486                 };
487
488                 var colStyle = table.className.search(/colstyle-([\S]+)/) != -1 ? table.className.match(/colstyle-([\S]+)/)[1] : false;
489                 var rowStyle = table.className.search(/rowstyle-([\S]+)/) != -1 ? table.className.match(/rowstyle-([\S]+)/)[1] : false;
490                 var iCBack   = table.className.search(/sortinitiatedcallback-([\S-]+)/) == -1 ? "sortInitiatedCallback" : table.className.match(/sortinitiatedcallback-([\S]+)/)[1];
491                 var cCBack   = table.className.search(/sortcompletecallback-([\S-]+)/) == -1 ? "sortCompleteCallback" : table.className.match(/sortcompletecallback-([\S]+)/)[1];
492                 iCBack = iCBack.replace("-", ".");
493                 cCBack = cCBack.replace("-", ".");
494                 fdTableSort.tableCache[table.id] = { hook:start, initiatedCallback:iCBack, completeCallback:cCBack, thList:[], colOrder:{}, data:data, identical:identical, colStyle:colStyle, rowStyle:rowStyle, noArrow:table.className.search(/no-arrow/) != -1 };
495                 sortableColumnNumbers = data = tr = td = th = trs = identical = identVal = null;
496         },
497         onUnload: function() {
498                 for(tbl in fdTableSort.tableCache) { fdTableSort.removeTableCache(tbl); };
499                 for(tbl in fdTableSort.tmpCache) { fdTableSort.removeTmpCache(tbl); };
500                 fdTableSort.removeEvent(window, "load", fdTableSort.initEvt);
501                 fdTableSort.removeEvent(window, "unload", fdTableSort.onUnload);
502                 fdTableSort.tmpCache = fdTableSort.tableCache = null;
503         },
504         addThNode: function() {
505                 var dataObj = fdTableSort.tableCache[fdTableSort.tableId];
506                 var pos     = fdTableSort.thNode.className.match(/fd-column-([0-9]+)/)[1];
507                 var alt     = false;
508
509                 if(!fdTableSort.multi) {
510                         if(dataObj.colStyle) {
511                                 var len = dataObj.thList.length;
512                                 for(var i = 0; i < len; i++) {
513                                         dataObj.colOrder[dataObj.thList[i].className.match(/fd-column-([0-9]+)/)[1]] = false;
514                                 };
515                         };
516                         if(dataObj.thList.length && dataObj.thList[0] == fdTableSort.thNode) alt = true;
517                         dataObj.thList = [];
518                 };
519
520                 var found = false;
521                 var l = dataObj.thList.length;
522
523                 for(var i = 0, n; n = dataObj.thList[i]; i++) {
524                         if(n == fdTableSort.thNode) {
525                                 found = true;
526                                 break;
527                         };
528                 };
529
530                 if(!found) {
531                         dataObj.thList.push(fdTableSort.thNode);
532                         if(dataObj.colStyle) { dataObj.colOrder[pos] = true; };
533                 };
534
535                 var ths = document.getElementById(fdTableSort.tableId).getElementsByTagName("th");
536                 for(var i = 0, th; th = ths[i]; i++) {
537                         found = false;
538                         for(var z = 0, n; n = dataObj.thList[z]; z++) {
539                                 if(n == th) {
540                                         found = true;
541                                         break;
542                                 };
543                         };
544                         if(!found) {
545                                 fdTableSort.removeClass(th, "(forwardSort|reverseSort)");
546                                 if(!dataObj.noArrow) {
547                                         span = th.getElementsByTagName('span');
548                                         if(span.length) {
549                                                 span = span[0];
550                                                 while(span.firstChild) span.removeChild(span.firstChild);
551                                         };
552                                 };
553                         };
554                 };
555
556                 if(dataObj.thList.length > 1) {
557                         classToAdd = fdTableSort.thNode.className.search(/forwardSort/) != -1 ? "reverseSort" : "forwardSort";
558                         fdTableSort.removeClass(fdTableSort.thNode, "(forwardSort|reverseSort)");
559                         fdTableSort.addClass(fdTableSort.thNode, classToAdd);
560                         dataObj.pos = -1
561                 } else if(alt) { dataObj.pos = fdTableSort.thNode };
562         },
563         initSort: function(noCallback, ident) {
564                 var thNode      = fdTableSort.thNode;
565                 var tableElem   = document.getElementById(fdTableSort.tableId);
566
567                 if(!(fdTableSort.tableId in fdTableSort.tableCache)) { fdTableSort.prepareTableData(document.getElementById(fdTableSort.tableId)); };
568
569                 fdTableSort.addThNode();
570
571                 if(!noCallback) { fdTableSort.doCallback(true); };
572
573                 fdTableSort.pos = thNode.className.match(/fd-column-([0-9]+)/)[1];
574                 var dataObj     = fdTableSort.tableCache[tableElem.id];
575                 var lastPos     = dataObj.pos && dataObj.pos.className ? dataObj.pos.className.match(/fd-column-([0-9]+)/)[1] : -1;
576                 var len1        = dataObj.data.length;
577                 var len2        = dataObj.data.length > 0 ? dataObj.data[0].length - 1 : 0;
578                 var identical   = dataObj.identical[fdTableSort.pos];
579                 var classToAdd  = "forwardSort";
580
581                 if(dataObj.thList.length > 1) {
582                         var js  = "var sortWrapper = function(a,b) {\n";
583                         var l   = dataObj.thList.length;
584                         var cnt = 0;
585                         var e,d,th,p,f;
586
587                         for(var i=0; i < l; i++) {
588                                 th = dataObj.thList[i];
589                                 p  = th.className.match(/fd-column-([0-9]+)/)[1];
590                                 if(dataObj.identical[p]) { continue; };
591                                 cnt++;
592
593                                 if(th.className.match(/sortable-(numeric|currency|date|keep)/)) {
594                                         f = "fdTableSort.sortNumeric";
595                                 } else if(th.className.match('sortable-text')) {
596                                         f = "fdTableSort.sortText";
597                                 } else if(th.className.search(/sortable-([a-zA-Z\_]+)/) != -1 && th.className.match(/sortable-([a-zA-Z\_]+)/)[1] in window) {
598                                         f = "window['" + th.className.match(/sortable-([a-zA-Z\_]+)/)[1] + "']";
599                                 } else  f = "fdTableSort.sortText";
600
601                                 e = "e" + i;
602                                 d = th.className.search('forwardSort') != -1 ? "a,b" : "b,a";
603                                 js += "fdTableSort.pos   = " + p + ";\n";
604                                 js += "var " + e + " = "+f+"(" + d +");\n";
605                                 js += "if(" + e + ") return " + e + ";\n";
606                                 js += "else { \n";
607                         };
608
609                         js += "return 0;\n";
610
611                         for(var i=0; i < cnt; i++) {
612                                 js += "};\n";
613                         };
614
615                         if(cnt) js += "return 0;\n";
616                         js += "};\n";
617
618                         eval(js);
619                         dataObj.data.sort(sortWrapper);
620                         identical = false;
621                 } else if((lastPos == fdTableSort.pos && !identical) || (thNode.className.search(/sortable-keep/) != -1 && lastPos == -1)) {
622                         dataObj.data.reverse();
623                         classToAdd = thNode.className.search(/reverseSort/) != -1 ? "forwardSort" : "reverseSort";
624                         if(thNode.className.search(/sortable-keep/) != -1 && lastPos == -1) fdTableSort.tableCache[tableElem.id].pos = thNode;
625                 } else {
626                         fdTableSort.tableCache[tableElem.id].pos = thNode;
627                         classToAdd = thNode.className.search(/forwardSort/) != -1 ? "reverseSort" : "forwardSort";
628                         if(!identical) {
629                                 if(thNode.className.match(/sortable-(numeric|currency|date|keep)/)) {
630                                         dataObj.data.sort(fdTableSort.sortNumeric);
631                                 } else if(thNode.className.match('sortable-text')) {
632                                         dataObj.data.sort(fdTableSort.sortText);
633                                 } else if(thNode.className.search(/sortable-([a-zA-Z\_]+)/) != -1 && thNode.className.match(/sortable-([a-zA-Z\_]+)/)[1] in window) {
634                                         dataObj.data.sort(window[thNode.className.match(/sortable-([a-zA-Z\_]+)/)[1]]);
635                                 };
636
637                                 if(thNode.className.search(/(^|\s)favour-reverse($|\s)/) != -1) {
638                                         classToAdd = classToAdd == "forwardSort" ? "reverseSort" : "forwardSort";
639                                         dataObj.data.reverse();
640                                 };
641                         };
642                 };
643                 if(ident) { identical = false; };
644                 if(dataObj.thList.length == 1) {
645                         fdTableSort.removeClass(thNode, "(forwardSort|reverseSort)");
646                         fdTableSort.addClass(thNode, classToAdd);
647                 };
648                 if(!dataObj.noArrow) {
649                         var span = fdTableSort.thNode.getElementsByTagName('span')[0];
650                         if(span.firstChild) span.removeChild(span.firstChild);
651                         span.appendChild(document.createTextNode(fdTableSort.thNode.className.search(/forwardSort/) != -1 ? " \u2193" : " \u2191"));
652                 };
653                 if(!dataObj.rowStyle && !dataObj.colStyle && identical) {
654                         fdTableSort.removeSortActiveClass();
655                         if(!noCallback) { fdTableSort.doCallback(false); };
656                         fdTableSort.thNode = null;
657                         return;
658                 };
659                 if("tablePaginater" in window && tablePaginater.tableIsPaginated(fdTableSort.tableId)) {
660                         tablePaginater.redraw(fdTableSort.tableId, identical);
661                 } else {
662                         fdTableSort.redraw(fdTableSort.tableId, identical);
663                 };
664                 fdTableSort.removeSortActiveClass();
665                 if(!noCallback) { fdTableSort.doCallback(false); };
666                 fdTableSort.thNode = null;
667         },
668         redraw: function(tableid, identical) {
669                 if (debug) plc_message ('entering tablesort.redraw');
670                 if(!tableid || !(tableid in fdTableSort.tableCache)) { return; };
671                 var dataObj     = fdTableSort.tableCache[tableid];
672                 var data        = dataObj.data;
673                 var len1        = data.length;
674                 var len2        = len1 ? data[0].length - 1 : 0;
675                 var hook        = dataObj.hook;
676                 var colStyle    = dataObj.colStyle;
677                 var rowStyle    = dataObj.rowStyle;
678                 var colOrder    = dataObj.colOrder;
679                 var highLight   = 0;
680                 var reg         = /(^|\s)invisibleRow(\s|$)/;
681                 var tr, tds;
682
683                 for(var i = 0; i < len1; i++) {
684                         tr = data[i][len2];
685                         if(colStyle) {
686                                 tds = tr.cells;
687                                 for(thPos in colOrder) {
688                                         if(!colOrder[thPos]) fdTableSort.removeClass(tds[thPos], colStyle);
689                                         else fdTableSort.addClass(tds[thPos], colStyle);
690                                 };
691                         };
692                         if(!identical) {
693                                 if(rowStyle && tr.className.search(reg) == -1) {
694                                         if(highLight++ & 1) fdTableSort.addClass(tr, rowStyle);
695                                         else fdTableSort.removeClass(tr, rowStyle);
696                                 };
697
698                                 // Netscape 8.1.2 requires the removeChild call or it freaks out, so add the line if you want to support this browser
699                                 // hook.removeChild(tr);
700                                 hook.appendChild(tr);
701                         };
702                 };
703                 tr = tds = hook = null;
704                 if (debug) plc_message ('exiting tablesort.redraw');
705         },
706         getInnerText: function(el, allowBrTags) {
707                 if (typeof el == "string" || typeof el == "undefined") return el;
708                 if(el.innerText) return el.innerText;
709                 var txt = '', i;
710                 for(i = el.firstChild; i; i = i.nextSibling) {
711                         if(allowBrTags && i.nodeName && i.nodeName == "BR") txt += "<br />";
712                         else if(i.nodeType == 3)       txt += i.nodeValue;
713                         else if(i.nodeType == 1)       txt += fdTableSort.getInnerText(i);
714                 };
715                 return txt;
716         },
717         dateFormat: function(dateIn, favourDMY) {
718                 var dateTest = [
719                         { regExp:/^(0?[1-9]|1[012])([- \/.])(0?[1-9]|[12][0-9]|3[01])([- \/.])((\d\d)?\d\d)$/, d:3, m:1, y:5 },  // mdy
720                         { regExp:/^(0?[1-9]|[12][0-9]|3[01])([- \/.])(0?[1-9]|1[012])([- \/.])((\d\d)?\d\d)$/, d:1, m:3, y:5 },  // dmy
721                         { regExp:/^(\d\d\d\d)([- \/.])(0?[1-9]|1[012])([- \/.])(0?[1-9]|[12][0-9]|3[01])$/, d:5, m:3, y:1 }      // ymd
722                         ];
723                 var start, cnt = 0, numFormats = dateTest.length;
724                 while(cnt < numFormats) {
725                         start = (cnt + (favourDMY ? numFormats + 1 : numFormats)) % numFormats;
726                         if(dateIn.match(dateTest[start].regExp)) {
727                                 res = dateIn.match(dateTest[start].regExp);
728                                 y = res[dateTest[start].y];
729                                 m = res[dateTest[start].m];
730                                 d = res[dateTest[start].d];
731                                 if(m.length == 1) m = "0" + String(m);
732                                 if(d.length == 1) d = "0" + String(d);
733                                 if(y.length != 4) y = (parseInt(y) < 50) ? "20" + String(y) : "19" + String(y);
734
735                                 return y+String(m)+d;
736                         };
737                         cnt++;
738                 };
739                 return 0;
740         },
741         sortNumeric:function(a,b) {
742                 var aa = a[fdTableSort.pos];
743                 var bb = b[fdTableSort.pos];
744                 if(aa == bb) return 0;
745                 if(aa === "" && !isNaN(bb)) return -1;
746                 if(bb === "" && !isNaN(aa)) return 1;
747                 return aa - bb;
748         },
749         sortText:function(a,b) {
750                 var aa = a[fdTableSort.pos];
751                 var bb = b[fdTableSort.pos];
752                 if(aa == bb) return 0;
753                 if(aa < bb)  return -1;
754                 return 1;
755         }
756 };
757 })();
758 fdTableSort.addEvent(window, "load",   fdTableSort.initEvt);
759 fdTableSort.addEvent(window, "unload", fdTableSort.onUnload);