1/* 2 based on code from http://www.kryogenix.org/code/browser/sorttable/ by Stuart Langridge 3 (distributed under the condisions of MIT licence from http://www.kryogenix.org/code/browser/licence.html) 4 see 5 2007-2016 by oiv (Otto Vainio at otto@valjakko.net) 6 2016-? by vaxquis AKA FyiurAmron (spamove@gmail.com) 7 */ 8 9// 10 11var stIsIE = /*@cc_on!@*/false; 12//var tableid = 0; 13 14var sorttable = { 15 reinit: function () { 16 arguments.callee.done = true; 17 18 if ( !document.createElement || !document.getElementsByTagName ) { 19 return; 20 } 21 22 var elems = document.getElementsByTagName( "table" ); 23 var elem; 24 for( var i = 0; i < elems.length; i++ ) { 25 elem = elems[i]; 26 if ( jQuery(elem).hasClass("sortable") ) { 27 sorttable.makeSortable( elem ); 28 } 29 } 30 elems = document.getElementsByTagName( "div" ); 31 for( var i = 0; i < elems.length; i++ ) { 32 elem = elems[i]; 33 if ( jQuery(elem).hasClass("sortable") ) { 34 sorttable.makeSortableDiv( elem ); 35 } 36 } 37 }, 38 init: function () { 39 if ( arguments.callee.done ) { 40 return; 41 } 42 sorttable.reinit(); 43 }, 44 makeSortableDiv: function ( div ) { 45 var childTables = div.getElementsByTagName( "table" ); 46 var elem; 47 for( var i = 0; i < childTables.length; i++ ) { 48 elem = childTables[i]; 49 var colid = div.className; 50 var patt1 = /\bcol_\d_[a-z]+/gi; 51 var overs = []; 52 if ( colid.search( patt1 ) !== -1 ) { 53 var overrides = colid.match( patt1 ); 54 for( var i = 0; i < overrides.length; i++ ) { 55 var entry = overrides[i]; 56 if ( entry !== "" ) { 57 try { 58 var tmp = entry.split( "_" ); 59 var ind = tmp[1]; 60 var val = tmp[2]; 61 overs[ind] = val; 62 } catch( e ) { 63 } 64 } 65 } 66 colid = colid.replace( patt1, '' ); 67 } 68 var patt2 = /\bsortbottom_?\d?/gi; 69 var bottoms = 0; 70 if ( colid.search( patt2 ) !== -1 ) { 71 var bs = colid.match( patt2 ); 72 try { 73 var tmp = bs[0].split( "_" ); 74 //var ind = tmp[1]; 75 var val = 1; 76 if ( tmp.length > 1 ) { 77 val = tmp[1]; 78 } 79 bottoms = val; 80 } catch( e ) { 81 } 82 } 83 var patt2ph = /\bthreephase/gi; 84 var ph2 = true; 85 if ( colid.search( patt2ph ) !== -1 ) { 86 ph2 = false; 87 } 88 89 sorttable.makeSortable( elem, overs, bottoms, ph2 ); 90 var pattdefault = /\bsortr?\d\d?/gi; 91 if ( colid.search( pattdefault ) !== -1 ) { 92 var mi = colid.match( pattdefault ); 93 colid = mi[0].replace( 'sort', '' ); 94 if ( colid !== '' ) { 95 colid = colid.trim(); 96 } 97 var revs = false; 98 if ( colid.search( /\br/ ) !== -1 ) { 99 revs = true; 100 colid = colid.replace( 'r', '' ); 101 } 102 sorttable.defaultSort( elem, colid, revs ); 103 } 104 } 105 }, 106 defaultSort: function ( table, colid, revs ) { 107// theadrow = table.tHead.rows[0].cells; 108 var havetHead = table.tHead; 109 var sindex = 1; 110 if ( havetHead ) { 111 sindex = 0; 112 } 113 var theadrow = table.rows[0].cells; 114 colid--; 115 var colname = "col" + colid; 116 // remove sorttable_sorted classes 117 var thiscell = false; 118 for( var i = 0; i < theadrow.length; i++ ) { 119 var cell = theadrow[i]; 120 var colclass = cell.className; 121 var classname = colclass.split( " " ); 122// if (cell.className==colname) 123 if ( classname[0] === colname ) { 124 thiscell = cell; 125 } 126 } 127 if ( thiscell === false ) { 128 return; 129 } 130 // build an array to sort. This is a Schwartzian transform thing, 131 // i.e., we "decorate" each row with the actual sort key, 132 // sort based on the sort keys, and then put the rows back in order 133 // which is a lot faster because you only do getInnerText once per row 134 var row_array = []; 135 var col = thiscell.sorttable_columnindex; 136 var rows = thiscell.sorttable_tbody.rows; 137 for( var j = sindex; j < rows.length; j++ ) { 138 row_array[row_array.length] = [sorttable.getInnerText( rows[j].cells[col] ), rows[j]]; 139 } 140 /* If you want a stable sort, uncomment the following line */ 141 //sorttable.shaker_sort(row_array, this.sorttable_sortfunction); 142 /* and comment out this one */ 143 row_array.sort( thiscell.sorttable_sortfunction ); 144 145 var tb = thiscell.sorttable_tbody; 146 for( var jj = 0; jj < row_array.length; jj++ ) { 147 tb.appendChild( row_array[jj][1] ); 148 } 149 150 if ( revs ) { 151 sorttable.reverse( thiscell.sorttable_tbody, sindex ); 152 jQuery(thiscell).addClass( "sorttable_sorted_reverse" ); 153 } else { 154 jQuery(thiscell).addClass( "sorttable_sorted" ); 155 } 156 }, 157 makeSortable: function ( table, overrides, bottoms, ph2 ) { 158// tableid++; 159 /* 160 if (table.getElementsByTagName('thead').length === 0) { 161 // table doesn't have a tHead. Since it should have, create one and 162 // put the first table row in it. 163 the = document.createElement('thead'); 164 the.appendChild(table.rows[0]); 165 table.insertBefore(the,table.firstChild); 166 } 167 */ 168 // Safari doesn't support table.tHead, sigh 169 /* 170 if (table.tHead === null) {table.tHead = table.getElementsByTagName('thead')[0];} 171 172 if (table.tHead.rows.length != 1) {return;} // can't cope with two header rows 173 */ 174// table.tHead.className += ' tableid'+tableid; 175 176 // Sorttable v1 put rows with a class of "sortbottom" at the bottom (as 177 // "total" rows, for example). This is B&R, since what you're supposed 178 // to do is put them in a tfoot. So, if there are sortbottom rows, 179 // for backwards compatibility, move them to tfoot (creating it if needed). 180 181 var sortbottomrows = []; 182 if ( bottoms > 0 ) { 183 var frombottom = table.rows.length - bottoms; 184 for( var i = table.rows.length - 1; i >= frombottom; i-- ) { 185// if (bottoms<frombottom) { 186 sortbottomrows[sortbottomrows.length] = table.rows[i]; 187// } 188// frombottom++; 189 } 190 if ( sortbottomrows ) { 191 var tfo; 192 if ( table.tFoot === null ) { 193 // table doesn't have a tfoot. Create one. 194 tfo = document.createElement( 'tfoot' ); 195 table.appendChild( tfo ); 196 } 197 for( var ii = sortbottomrows.length - 1; ii >= 0; ii-- ) { 198 tfo.appendChild( sortbottomrows[ii] ); 199 } 200 //delete sortbottomrows; 201 } 202 } 203 // work through each column and calculate its type 204 var havetHead = table.tHead; 205 var sindex = 1; 206 if ( havetHead ) { 207 sindex = 0; 208 } 209 var headrow = table.rows[0].cells; 210 211 for( var i = 0; i < headrow.length; i++ ) { 212 // manually override the type with a sorttable_type attribute 213 var colOptions = ""; 214 if ( overrides[i + 1] ) 215 { 216 colOptions = overrides[i + 1]; 217 } 218 if ( !colOptions.match( /\bnosort\b/ ) ) { // skip this col 219 var mtch = colOptions.match( /\b[a-z0-9]+\b/ ); 220 var override; 221 if ( mtch ) { 222 override = mtch[0]; 223 } 224 if ( mtch && typeof sorttable["sort_" + override] === 'function' ) { 225 headrow[i].sorttable_sortfunction = sorttable["sort_" + override]; 226 } else { 227 headrow[i].sorttable_sortfunction = sorttable.guessType( table, i ); 228 } 229 /* 230 if (!headrow[i].className.match(/\bsorttable_nosort\b/)) { // skip this col 231 mtch = headrow[i].className.match(/\bsorttable_([a-z0-9]+)\b/); 232 if (mtch) { override = mtch[1]; } 233 if (mtch && typeof sorttable["sort_"+override] == 'function') { 234 headrow[i].sorttable_sortfunction = sorttable["sort_"+override]; 235 } else { 236 headrow[i].sorttable_sortfunction = sorttable.guessType(table,i); 237 } 238 */ 239 // make it clickable to sort 240 headrow[i].sorttable_columnindex = i; 241 headrow[i].sorttable_tbody = table.tBodies[0]; 242 headrow[i].sindex = sindex; 243 244 jQuery( headrow[i] ).click( function () { 245 var theadrow = this.parentNode; 246 var jqt = jQuery( this ); 247 if ( jqt.hasClass( "sorttable_sorted" ) ) { 248 // if we're already sorted by this column, just reverse the table 249 sorttable.reverse( this.sorttable_tbody, this.sindex ); 250 jqt.removeClass( "sorttable_sorted" ); 251 jqt.addClass( "sorttable_sorted_reverse" ); 252 return; 253 } 254 if ( jqt.hasClass( "sorttable_sorted_reverse" ) ) { 255 if ( !ph2 ) { 256 sorttable.original_order( this.sorttable_tbody, this.sindex ); 257 var list = theadrow.childNodes; 258 for( var i = 0; i < list.length; i++ ) { 259 var cell = list[i]; 260 if ( cell.nodeType === 1 ) { // an element 261 var cc = jQuery( cell ); 262 cc.removeClass( "sorttable_sorted" ); 263 cc.removeClass( "sorttable_sorted_reverse" ); 264 } 265 } 266 return; 267 } else { 268 // if we're already sorted by this column in reverse, just re-reverse the table 269 sorttable.reverse( this.sorttable_tbody, this.sindex ); 270 jqt.removeClass( "sorttable_sorted_reverse" ); 271 jqt.addClass( "sorttable_sorted" ); 272 return; 273 } 274 } 275 276 // remove sorttable_sorted classes 277 var list = theadrow.childNodes; 278 for( var i = 0; i < list.length; i++ ) { 279 var cell = list[i]; 280 if ( cell.nodeType === 1 ) { // an element 281 var cc = jQuery( cell ); 282 cc.removeClass( "sorttable_sorted" ); 283 cc.removeClass( "sorttable_sorted_reverse" ); 284 } 285 } 286 jqt.addClass( "sorttable_sorted" ); 287 288 // build an array to sort. This is a Schwartzian transform thing, 289 // i.e., we "decorate" each row with the actual sort key, 290 // sort based on the sort keys, and then put the rows back in order 291 // which is a lot faster because you only do getInnerText once per row 292 var row_array = []; 293 var col = this.sorttable_columnindex; 294 var rows = this.sorttable_tbody.rows; 295 sindex = this.sindex; 296 for( var j = sindex; j < rows.length; j++ ) { 297 row_array[row_array.length] = [sorttable.getInnerText( rows[j].cells[col] ), rows[j]]; 298 } 299 /* If you want a stable sort, uncomment the following line */ 300 //sorttable.shaker_sort(row_array, this.sorttable_sortfunction); 301 /* and comment out this one */ 302 row_array.sort( this.sorttable_sortfunction ); 303 304 var tb = this.sorttable_tbody; 305 for( var j3 = 0; j3 < row_array.length; j3++ ) { 306 tb.appendChild( row_array[j3][1] ); 307 } 308 309 //delete row_array; 310 } ); 311 } 312 } 313 }, 314 guessType: function ( table, column ) { 315 // guess the type of a column based on its first non-blank row 316 var textCnt = 0; 317 var numCnt = 0; 318 var dateCnt = 0; 319 var ipCnt = 0; 320 321 for( var i = 0; i < table.tBodies[0].rows.length; i++ ) { 322 var text = sorttable.getInnerText( table.tBodies[0].rows[i].cells[column] ); 323 if ( text !== "" ) { 324 if ( text.match( /^([01]?\d\d?|2[0-4]\d|25[0-5])\.([01]?\d\d?|2[0-4]\d|25[0-5])\.([01]?\d\d?|2[0-4]\d|25[0-5])\.([01]?\d\d?|2[0-4]\d|25[0-5])$/ ) ) { 325 ipCnt++; 326 } else if ( text.match( /^[\-\+]?.?\d*[\d,.]?\d+.?$/ ) ) { 327 numCnt++; 328 } else if ( !isNaN( new Date( text ).getTime() ) ) { 329 dateCnt++; 330 } else { // not a date (nor IP nor number) 331 textCnt++; 332 } 333 } 334 } 335 if ( textCnt > numCnt && textCnt > ipCnt && textCnt > dateCnt ) 336 return sorttable.sort_alpha; 337 if ( numCnt > ipCnt && numCnt > dateCnt ) 338 return sorttable.sort_numeric; 339 if ( ipCnt > dateCnt ) 340 return sorttable.sort_ipaddr; 341 return sorttable.sort_date; 342 }, 343 getInnerText: function ( node ) { 344 // gets the text we want to use for sorting for a cell. 345 // strips leading and trailing whitespace. 346 // this is *not* a generic getInnerText function; it's special to sorttable. 347 // for example, you can override the cell text with a customkey attribute. 348 // it also gets .value for <input> fields. 349 if ( !node ) { 350 return ''; 351 } 352 var hasInputs = ( typeof node.getElementsByTagName === "function" ) && 353 node.getElementsByTagName( "input" ).length; 354 if ( node.getAttribute( "sorttable_customkey" ) !== null ) { 355 return node.getAttribute( "sorttable_customkey" ); 356 } else if ( typeof node.textContent !== "undefined" && !hasInputs ) { 357 return node.textContent.replace( /^\s+|\s+$/g, '' ); 358 } else if ( typeof node.innerText !== "undefined" && !hasInputs ) { 359 return node.innerText.replace( /^\s+|\s+$/g, '' ); 360 } else if ( typeof node.text !== "undefined" && !hasInputs ) { 361 return node.text.replace( /^\s+|\s+$/g, '' ); 362 } else { 363 switch ( node.nodeType ) { 364 case 3: 365 return ( node.nodeName.toLowerCase() === "input" ) ? node.value.replace( /^\s+|\s+$/g, '' ) : ''; 366 case 4: 367 return node.nodeValue.replace( /^\s+|\s+$/g, '' ); 368 case 1: 369 case 11: 370 var innerText = ''; 371 for( var i = 0; i < node.childNodes.length; i++ ) { 372 innerText += sorttable.getInnerText( node.childNodes[i] ); 373 } 374 return innerText.replace( /^\s+|\s+$/g, '' ); 375 default: 376 return ''; 377 } 378 } 379 }, 380 reverse: function ( tbody, sindex ) { 381 // reverse the rows in a tbody 382 var newrows = []; 383 for( var i = sindex; i < tbody.rows.length; i++ ) { 384 newrows[newrows.length] = tbody.rows[i]; 385 } 386 for( var i = newrows.length - 1; i >= 0; i-- ) { 387 tbody.appendChild( newrows[i] ); 388 } 389 //delete newrows; 390 }, 391 original_order: function ( tbody, isindex ) { 392 // build an array to sort. This is a Schwartzian transform thing, 393 // i.e., we "decorate" each row with the actual sort key, 394 // sort based on the sort keys, and then put the rows back in order 395 // which is a lot faster because you only do getInnerText once per row 396 var row_array = []; 397 var rows = tbody.rows; 398 var sindex = isindex; 399 for( var j = sindex; j < rows.length; j++ ) { 400 row_array[row_array.length] = [rows[j].className, rows[j]]; 401 } 402 /* If you want a stable sort, uncomment the following line */ 403 //sorttable.shaker_sort(row_array, this.sorttable_sortfunction); 404 /* and comment out this one */ 405 row_array.sort( sorttable.sort_alpha ); 406 407 var tb = tbody; 408 for( var j3 = 0; j3 < row_array.length; j3++ ) { 409 tb.appendChild( row_array[j3][1] ); 410 } 411 412 //delete row_array; 413 }, 414 /* sort functions 415 each sort function takes two parameters, a and b 416 you are comparing a[0] and b[0] */ 417 sort_ipaddr: function ( a, b ) { 418 var aa = a[0].split( ".", 4 ); 419 var bb = b[0].split( ".", 4 ); 420 var resulta = aa[0] * 0x1000000 + aa[1] * 0x10000 + aa[2] * 0x100 + aa[3] * 1; 421 var resultb = bb[0] * 0x1000000 + bb[1] * 0x10000 + bb[2] * 0x100 + bb[3] * 1; 422 return resulta - resultb; 423 }, 424 sort_numeric: function ( a, b ) { 425 if ( a[0] === "" ) { 426 return -1; 427 } 428 if ( b[0] === "" ) { 429 return 1; 430 } 431 var aa = parseFloat( a[0].replace( ",", "." ).replace( /[^0-9.\-]/g, "" ) ); 432 if ( isNaN( aa ) ) { 433 aa = Number.NEGATIVE_INFINITY; 434 } 435 var bb = parseFloat( b[0].replace( ",", "." ).replace( /[^0-9.\-]/g, "" ) ); 436 if ( isNaN( bb ) ) { 437 bb = Number.NEGATIVE_INFINITY; 438 } 439 return aa - bb; 440 }, 441 sort_alpha: function ( a, b ) { 442 return a[0].localeCompare( b[0] ); 443 }, 444 sort_date: function ( a, b ) { 445 var aa = new Date( a[0] ), bb = new Date( b[0] ); 446 return ( aa > bb ) - ( aa < bb ); 447 }, 448 shaker_sort: function ( list, comp_func ) { 449 // A stable sort function to allow multi-level sorting of data 450 // see: http://en.wikipedia.org/wiki/Cocktail_sort 451 // thanks to Joseph Nahmias 452 var b = 0; 453 var t = list.length - 1; 454 var swap = true; 455 var q; 456 457 while( swap ) { 458 swap = false; 459 for( var i = b; i < t; ++i ) { 460 if ( comp_func( list[i], list[i + 1] ) > 0 ) { 461 q = list[i]; 462 list[i] = list[i + 1]; 463 list[i + 1] = q; 464 swap = true; 465 } 466 } // for 467 t--; 468 469 if ( !swap ) { 470 break; 471 } 472 473 for( var i = t; i > b; --i ) { 474 if ( comp_func( list[i], list[i - 1] ) < 0 ) { 475 q = list[i]; 476 list[i] = list[i - 1]; 477 list[i - 1] = q; 478 swap = true; 479 } 480 } // for 481 b++; 482 483 } // while(swap) 484 } 485 486}; 487 488if ( typeof ( window.addEvent ) !== "undefined" ) { 489 window.addEvent( window, "load", sorttable.init ); 490} else { 491 jQuery( function () { 492 sorttable.init(); 493 } ); 494} 495