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