1/* 2 TableSort revisited v2.9 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 28var fdTableSort = { 29 30 regExp_Currency: /^[£$¥¤]/, 31 regExp_Number: /^(\-)?[0-9]+(\.[0-9]*)?$/, 32 pos: -1, 33 uniqueHash: 1, 34 thNode: null, 35 tableCache: {}, 36 tableId: null, 37 38 addEvent: function(obj, type, fn) { 39 if( obj.attachEvent ) { 40 obj["e"+type+fn] = fn; 41 obj[type+fn] = function(){obj["e"+type+fn]( window.event );} 42 obj.attachEvent( "on"+type, obj[type+fn] ); 43 } else 44 obj.addEventListener( type, fn, false ); 45 }, 46 47 stopEvent: function(e) { 48 e = e || window.event; 49 50 if(e.stopPropagation) { 51 e.stopPropagation(); 52 e.preventDefault(); 53 } 54 /*@cc_on@*/ 55 /*@if(@_win32) 56 e.cancelBubble = true; 57 e.returnValue = false; 58 /*@end@*/ 59 return false; 60 }, 61 62 init: function() { 63 if (!document.getElementsByTagName) return; 64 65 var tables = document.getElementsByTagName('table'); 66 var sortable, headers, thtext, aclone, a, span, columnNum, noArrow, reverseSortOnInit; 67 68 a = document.createElement("a"); 69 a.href = "#"; 70 a.onkeypress = fdTableSort.keyWrapper; 71 72 span = document.createElement("span"); 73 74 for(var t = 0, tbl; tbl = tables[t]; t++) { 75 76 headers = fdTableSort.getTableHeaders(tbl); 77 78 sortable = false; 79 columnNum = tbl.className.search(/sortable-onload-([0-9]+)/) != -1 ? parseInt(tbl.className.match(/sortable-onload-([0-9]+)/)[1]) - 1 : -1; 80 showArrow = tbl.className.search(/no-arrow/) == -1; 81 reverse = tbl.className.search(/sortable-onload-([0-9]+)-reverse/) != -1; 82 83 // Remove any old dataObj for this table (tables created from an ajax callback require this) 84 if(tbl.id && tbl.id in fdTableSort.tableCache) delete fdTableSort.tableCache[tbl.id]; 85 86 var colCnt = -1; 87 88 for (var z=0, th; th = headers[z]; z++) { 89 90 colCnt += th.getAttribute("colspan") ? Number(th.getAttribute("colspan")) : 1; 91 92 if(th.className && th.className.match('sortable') && (!th.getAttribute("colspan") || th.getAttribute("colspan") == 1)) { 93 94 // Remove previously applied classes for the ajaxers also 95 th.className = th.className.replace(/forwardSort|reverseSort/, ""); 96 97 if(colCnt == columnNum) sortable = th; 98 thtext = fdTableSort.getInnerText(th); 99 100 while(th.firstChild) th.removeChild(th.firstChild); 101 102 // Create the link
103 aclone = a.cloneNode(true); 104 aclone.appendChild(document.createTextNode(thtext)); 105 aclone.title = "Sort on " + thtext; 106 a.onclick = th.onclick = fdTableSort.clickWrapper; 107 th.appendChild(aclone); 108 109 // Add the span if needs be 110 if(showArrow) th.appendChild(span.cloneNode(false)); 111 112 var cn = "fd-column-" + colCnt; 113 th.className = th.className.replace(/fd-identical|fd-not-identical/, "").replace(/fd-column-([0-9]+)/, "") + " " + cn; 114 fdTableSort.disableSelection(th); 115 }; 116 }; 117 118 if(sortable) { 119 fdTableSort.thNode = sortable; 120 fdTableSort.initSort(); 121 if(reverse) { 122 fdTableSort.thNode = sortable; 123 fdTableSort.initSort(); 124 }; 125 }; 126 }; 127 }, 128 129 disableSelection: function(element) { 130 element.onselectstart = function() { 131 return false; 132 }; 133 element.unselectable = "on"; 134 element.style.MozUserSelect = "none"; 135 }, 136 137 getTableHeaders: function(tbl) { 138 var headers; 139 var thead = tbl.getElementsByTagName('thead'); 140 141 if(thead && thead.length) { 142 thead = thead[0]; 143 headers = thead.getElementsByTagName('tr'); 144 headers = headers[headers.length - 1].getElementsByTagName('th'); 145 } else { 146 headers = tbl.getElementsByTagName('th'); 147 } 148 return headers; 149 }, 150 151 countColumns: function(thList) { 152 var colCnt = 0; 153 for(var i = 0, th; th = thList[i]; i++) { 154 colCnt += th.getAttribute("colspan") ? Number(th.getAttribute("colspan")) : 1; 155 }; 156 157 return colCnt; 158 }, 159 160 getTH: function(thList, pos) { 161 var thList = fdTableSort.thNode.parentNode.getElementsByTagName("th"); 162 163 for(var i = 0, th; th = thList[i]; i++) { 164 var re = new RegExp( "(fd-column-" + pos + ")([^0-9]+)" ); 165 if(th.className.search(re) != -1) return th; 166 }; 167 }, 168 169 clickWrapper: function(e) { 170 e = e || window.event; 171 if(fdTableSort.thNode == null) { 172 fdTableSort.thNode = this; 173 fdTableSort.addSortActiveClass(); 174 setTimeout("fdTableSort.initSort()",5); 175 176 }; 177 return fdTableSort.stopEvent(e); 178 }, 179 180 keyWrapper: function(e) { 181 e = e || window.event; 182 var kc = e.keyCode != null ? e.keyCode : e.charCode; 183 if(kc == 13) { 184 var targ = this; 185 while(targ.tagName.toLowerCase() != "th") targ = targ.parentNode; 186 187 fdTableSort.thNode = targ; 188 fdTableSort.addSortActiveClass(); 189 setTimeout("fdTableSort.initSort()",5); 190 191 return fdTableSort.stopEvent(e); 192 }; 193 return true; 194 }, 195 196 jsWrapper: function(tableid, colNum) { 197 var table = document.getElementById(tableid); 198 fdTableSort.thNode = fdTableSort.getTH(table.getElementsByTagName('th'), colnum); 199 if(!fdTableSort.thNode || fdTableSort.thNode.className.search(/fd-column/) == -1) return false;
200 fdTableSort.addSortActiveClass(); 201 fdTableSort.initSort(); 202 }, 203 204 addSortActiveClass: function() { 205 if(fdTableSort.thNode == null) return; 206 fdTableSort.addClass(fdTableSort.thNode, "sort-active"); 207 fdTableSort.addClass(document.getElementsByTagName('body')[0], "sort-active"); 208 if("sortInitiatedCallback" in window) { 209 var tableElem = fdTableSort.thNode; 210 while(tableElem.tagName.toLowerCase() != 'table' && tableElem.parentNode) { 211 tableElem = tableElem.parentNode; 212 }; 213 sortInitiatedCallback(tableElem.id); 214 }; 215 }, 216 217 removeSortActiveClass: function() { 218 fdTableSort.removeClass(fdTableSort.thNode, "sort-active"); 219 fdTableSort.removeClass(document.getElementsByTagName('body')[0], "sort-active"); 220 if("sortCompleteCallback" in window) { 221 var tableElem = fdTableSort.thNode; 222 while(tableElem.tagName.toLowerCase() != 'table' && tableElem.parentNode) { 223 tableElem = tableElem.parentNode; 224 }; 225 sortCompleteCallback(tableElem.id); 226 }; 227 }, 228 229 addClass: function(e,c) { 230 if(new RegExp("(^|\\s)" + c + "(\\s|$)").test(e.className)) return; 231 e.className += ( e.className ? " " : "" ) + c; 232 }, 233 234 removeClass: function(e,c) { 235 e.className = !c ? "" : e.className.replace(new RegExp("(^|\\s*\\b[^-])"+c+"($|\\b(?=[^-]))", "g"), ""); 236 }, 237 238 prepareTableData: function(table) { 239 // Create a table id if needs be 240 if(!table.id) table.id = "fd-table-" + fdTableSort.uniqueHash++; 241 242 var data = []; 243 244 var start = table.getElementsByTagName('tbody'); 245 start = start.length ? start[0] : table; 246 247 var trs = start.getElementsByTagName('tr'); 248 var ths = fdTableSort.getTableHeaders(table); 249 250 var numberOfRows = trs.length; 251 var numberOfCols = fdTableSort.countColumns(ths); 252 253 var data = []; 254 var identical = new Array(numberOfCols); 255 var identVal = new Array(numberOfCols); 256 257 var tr, td, th, txt, tds, col, row; 258 259 var rowCnt = 0; 260 261 // Start to create the 2D matrix of data 262 for(row = 0; row < numberOfRows; row++) { 263 264 tr = trs[row]; 265 266 // Have we any th tags or are we in a tfoot ? 267 if(tr.getElementsByTagName('th').length > 0 || (tr.parentNode && tr.parentNode.tagName.toLowerCase() == "tfoot")) continue; 268 269 data[rowCnt] = []; 270 tds = tr.getElementsByTagName('td'); 271 col = -1; 272 273 for(var tmp = 0, th; th = ths[tmp]; tmp++) { 274 275 col += th.getAttribute("colspan") ? Number(th.getAttribute("colspan")) : 1; 276 277 if(th.className.search(/sortable/) == -1 || (th.getAttribute("colspan") && Number(th.getAttribute("colspan")) > 1)) continue; 278 279 td = tds[col]; 280 281 txt = fdTableSort.getInnerText(td) + " "; 282 283 txt = txt.replace(/^\s+/,'').replace(/\s+$/,''); 284 285 if(th.className.search(/sortable-date/) != -1) { 286 txt = fdTableSort.dateFormat(txt, th.className.search(/sortable-date-dmy/) != -1); 287 } else if(th.className.search(/sortable-numeric|sortable-currency/) != -1) { 288 txt = parseFloat(txt.replace(/[^0-9\.\-]/g,'')); 289 if(isNaN(txt)) txt = ""; 290 } else if(th.className.search(/sortable-text/) != -1) { 291 txt = txt.toLowerCase(); 292 } else if(th.className.search(/sortable-([a-zA-Z\_]+)/) != -1) { 293 if((th.className.match(/sortable-([a-zA-Z\_]+)/)[1] + "PrepareData") in window) { 294 txt = window[th.className.match(/sortable-([a-zA-Z\_]+)/)[1] + "PrepareData"](td, txt); 295 }; 296 } else { 297 if(txt != "") { 298 fdTableSort.removeClass(th, "sortable"); 299 if(fdTableSort.dateFormat(txt) != 0) { 300 fdTableSort.addClass(th, "sortable-date"); 301 txt = fdTableSort.dateFormat(txt); 302 } else if(txt.search(fdTableSort.regExp_Number) != -1 || txt.search(fdTableSort.regExp_Currency) != -1) { 303 fdTableSort.addClass(th, "sortable-numeric"); 304 txt = parseFloat(txt.replace(/[^0-9\.\-]/g,'')); 305 if(isNaN(txt)) txt = ""; 306 } else { 307 fdTableSort.addClass(th, "sortable-text"); 308 txt = txt.toLowerCase(); 309 }; 310 }; 311 }; 312 313 if(rowCnt > 0 && identVal[col] != txt) { 314 identical[col] = false;
315 }; 316 317 identVal[col] = txt; 318 data[rowCnt][col] = txt; 319 }; 320 321 // Add the tr for this row 322 data[rowCnt][numberOfCols] = tr; 323 324 // Increment the row count 325 rowCnt++; 326 } 327 328 // Get the row and column styles 329 var colStyle = table.className.search(/colstyle-([\S]+)/) != -1 ? table.className.match(/colstyle-([\S]+)/)[1] : false; 330 var rowStyle = table.className.search(/rowstyle-([\S]+)/) != -1 ? table.className.match(/rowstyle-([\S]+)/)[1] : false; 331 332 // Cache the data object for this table 333 fdTableSort.tableCache[table.id] = { data:data, pos:-1, identical:identical, colStyle:colStyle, rowStyle:rowStyle, noArrow:table.className.search(/no-arrow/) != -1 }; 334 }, 335 336 initSort: function() { 337 338 var span; 339 var thNode = fdTableSort.thNode; 340 341 // Get the table 342 var tableElem = fdTableSort.thNode; 343 while(tableElem.tagName.toLowerCase() != 'table' && tableElem.parentNode) { 344 tableElem = tableElem.parentNode; 345 }; 346 347 // If this is the first time that this table has been sorted, create the data object 348 if(!tableElem.id || !(tableElem.id in fdTableSort.tableCache)) { 349 fdTableSort.prepareTableData(tableElem); 350 }; 351 352 // Cache the table id 353 fdTableSort.tableId = tableElem.id; 354 355 // Get the column position using the className added earlier 356 fdTableSort.pos = thNode.className.match(/fd-column-([0-9]+)/)[1]; 357 358 // Grab the data object for this table 359 var dataObj = fdTableSort.tableCache[tableElem.id]; 360 361 // Get the position of the last column that was sorted 362 var lastPos = dataObj.pos; 363 364 // Get the stored data object for this table 365 var data = dataObj.data; 366 var colStyle = dataObj.colStyle; 367 var rowStyle = dataObj.rowStyle; 368 var len1 = data.length; 369 var len2 = data[0].length - 1; 370 var identical = dataObj.identical[fdTableSort.pos] == false ? false : true; 371 var noArrow = dataObj.noArrow; 372 373 if(lastPos != fdTableSort.pos && lastPos != -1) { 374 var th = fdTableSort.getTH(thNode.parentNode.getElementsByTagName('th'), lastPos); 375 376 fdTableSort.removeClass(th, "forwardSort"); 377 fdTableSort.removeClass(th, "reverseSort"); 378 if(!noArrow) { 379 // Remove arrow 380 span = th.getElementsByTagName('span')[0]; 381 while(span.firstChild) span.removeChild(span.firstChild); 382 }; 383 }; 384 385 // If the same column is being sorted then just reverse the data object contents. 386 var classToAdd = "forwardSort"; 387 388 if(lastPos == fdTableSort.pos && !identical) { 389 data.reverse(); 390 classToAdd = thNode.className.search(/reverseSort/) != -1 ? "forwardSort" : "reverseSort"; 391 } else { 392 fdTableSort.tableCache[tableElem.id].pos = fdTableSort.pos; 393 if(!identical) { 394 if(thNode.className.match(/sortable-numeric|sortable-currency|sortable-date/)) { 395 data.sort(fdTableSort.sortNumeric); 396 } else if(thNode.className.match('sortable-text')) { 397 data.sort(fdTableSort.sortText); 398 } else if(thNode.className.search(/sortable-([a-zA-Z\_]+)/) != -1 && thNode.className.match(/sortable-([a-zA-Z\_]+)/)[1] in window) { 399 data.sort(window[thNode.className.match(/sortable-([a-zA-Z\_]+)/)[1]]); 400 }; 401 }; 402 }; 403 404 fdTableSort.removeClass(thNode, "forwardSort"); 405 fdTableSort.removeClass(thNode, "reverseSort"); 406 fdTableSort.addClass(thNode, classToAdd); 407 408 if(!noArrow) { 409 var arrow = thNode.className.search(/forwardSort/) != -1 ? " \u2193" : " \u2191"; 410 span = thNode.getElementsByTagName('span')[0]; 411 while(span.firstChild) span.removeChild(span.firstChild); 412 span.appendChild(document.createTextNode(arrow)); 413 }; 414 415 if(!rowStyle && !colStyle && identical) { 416 fdTableSort.removeSortActiveClass(); 417 fdTableSort.thNode = null; 418 return; 419 } 420 421 var hook = tableElem.getElementsByTagName('tbody'); 422 hook = hook.length ? hook[0] : tableElem; 423 424 var td, tr; 425 426 for(var i = 0; i < len1; i++) { 427 tr = data[i][len2]; 428 if(colStyle) { 429 if(lastPos != -1) { 430 fdTableSort.removeClass(tr.getElementsByTagName('td')[lastPos], colStyle); 431 } 432 fdTableSort.addClass(tr.getElementsByTagName('td')[fdTableSort.pos], colStyle); 433 }; 434 if(!identical) { 435 436 if(rowStyle) { 437 if(i % 2) fdTableSort.addClass(tr, rowStyle); 438 else fdTableSort.removeClass(tr, rowStyle); 439 }; 440 441 hook.removeChild(tr); /* Netscape 8.1.2 requires the removeChild call */ 442 hook.appendChild(tr); 443 }; 444 }; 445 fdTableSort.removeSortActiveClass(); 446 fdTableSort.thNode = null; 447 }, 448 449 getInnerText: function(el) { 450 if (typeof el == "string" || typeof el == "undefined") return el; 451 if(el.innerText) return el.innerText; 452 453 var txt = '', i; 454 for (i = el.firstChild; i; i = i.nextSibling) { 455 if (i.nodeType == 3) txt += i.nodeValue; 456 else if (i.nodeType == 1) txt += fdTableSort.getInnerText(i); 457 }; 458 459 return txt; 460 }, 461 462 dateFormat: function(dateIn, favourDMY) { 463 var dateTest = [ 464 { 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 465 { 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 466 { 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 467 ]; 468 469 var start; 470 var cnt = 0; 471 472 while(cnt < 3) { 473 start = (cnt + (favourDMY ? 4 : 3)) % 3; 474 475 if(dateIn.match(dateTest[start].regExp)) { 476 res = dateIn.match(dateTest[start].regExp); 477 y = res[dateTest[start].y]; 478 m = res[dateTest[start].m]; 479 d = res[dateTest[start].d]; 480 if(m.length == 1) m = "0" + m; 481 if(d.length == 1) d = "0" + d; 482 if(y.length != 4) y = (parseInt(y) < 50) ? '20' + y : '19' + y; 483 484 return y+m+d; 485 } 486 487 cnt++; 488 } 489 490 return 0; 491 }, 492 493 sortDate: function(a,b) { 494 var aa = a[fdTableSort.pos]; 495 var bb = b[fdTableSort.pos]; 496 497 return aa - bb; 498 }, 499 500 sortNumeric:function (a,b) { 501 var aa = a[fdTableSort.pos]; 502 var bb = b[fdTableSort.pos]; 503 504 if(aa === "" && !isNaN(bb)) return -1; 505 else if(bb === "" && !isNaN(aa)) return 1; 506 else if(aa == bb) return 0; 507 508 return aa - bb; 509 }, 510 511 sortText:function (a,b) { 512 var aa = a[fdTableSort.pos]; 513 var bb = b[fdTableSort.pos]; 514 515 if(aa == bb) return 0; 516 if(aa < bb) return -1; 517 518 return 1; 519 } 520}; 521 522fdTableSort.addEvent(window, "load", fdTableSort.init); 523
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.