1/*! Widget: math - updated 12/1/2019 (v2.31.2) *//* 2* Requires tablesorter v2.16+ and jQuery 1.7+ 3* by Rob Garrison 4*/ 5/*jshint browser:true, jquery:true, unused:false */ 6/*global jQuery: false */ 7;( function( $ ) { 8 'use strict'; 9 10 var ts = $.tablesorter, 11 12 math = { 13 14 error: { 15 0 : 'Infinity result: Divide by zero', 16 1 : 'Need more than one element to make this calculation', 17 'undef' : 'No elements found' 18 }, 19 20 // value returned when calculation is not possible, e.g. no values, dividing by zero, etc. 21 invalid : function( c, name, errorIndex ) { 22 // name = function returning invalid results 23 // errorIndex = math.error index with an explanation of the error 24 console.warn( name, math.error[ errorIndex ] ); 25 return c && c.widgetOptions.math_none || ''; // text for cell 26 }, 27 28 events : ( 'tablesorter-initialized update updateAll updateRows addRows updateCell filterReset ' ) 29 .split(' ').join('.tsmath '), 30 31 processText : function( c, $cell ) { 32 var tmp, 33 wo = c.widgetOptions, 34 txt = ts.getElementText( c, $cell, math.getCellIndex( $cell ) ), 35 prefix = c.widgetOptions.math_prefix; 36 if (wo.math_textAttr) { 37 txt = $cell.attr(wo.math_textAttr) || txt; 38 } 39 if ( /</.test( prefix ) ) { 40 // prefix contains HTML; remove it & any text before using formatFloat 41 tmp = $( '<div>' + prefix + '</div>' ).text() 42 .replace(/\{content\}/g, '').trim(); 43 txt = txt.replace( tmp, '' ); 44 } 45 txt = ts.formatFloat( txt.replace( /[^\w,. \-()]/g, '' ), c.table ) || 0; 46 // isNaN('') => false 47 return isNaN( txt ) ? 0 : txt; 48 }, 49 50 // get all of the row numerical values in an arry 51 getRow : function( c, $el, hasFilter ) { 52 var $cells, 53 wo = c.widgetOptions, 54 arry = [], 55 $row = $el.closest( 'tr' ), 56 isFiltered = $row.hasClass( wo.filter_filteredRow || 'filtered' ); 57 if ( hasFilter ) { 58 $row = $row.filter( hasFilter ); 59 } 60 if ( hasFilter || !isFiltered ) { 61 $cells = $row.children().not( '[' + wo.math_dataAttrib + '=ignore]' ); 62 if ( wo.math_ignore.length ) { 63 $cells = $cells.filter( function() { 64 // using $.inArray is not optimal (needed for IE8) 65 return $.inArray( math.getCellIndex( $( this ) ), wo.math_ignore ) === -1; 66 }); 67 } 68 arry = $cells.not( $el ).map( function() { 69 return math.processText( c, $( this ) ); 70 }).get(); 71 } 72 return arry; 73 }, 74 75 // get all of the column numerical values in an arry 76 getColumn : function( c, $el, type, hasFilter ) { 77 var index, $t, $tr, len, $mathRows, mathAbove, 78 wo = c.widgetOptions, 79 arry = [], 80 $row = $el.closest( 'tr' ), 81 mathAttr = wo.math_dataAttrib, 82 mathIgnore = '[' + mathAttr + '=ignore]', 83 filtered = wo.filter_filteredRow || 'filtered', 84 cIndex = math.getCellIndex( $el ), 85 // get all rows to keep row indexing 86 $rows = c.$table.children( 'tbody' ).children(), 87 mathAttrs = [ 88 '[' + mathAttr + '^=above]', 89 '[' + mathAttr + '^=below]', 90 '[' + mathAttr + '^=col]', 91 '[' + mathAttr + '^=all]' 92 ]; 93 if ( type === 'above' ) { 94 len = $rows.index( $row ); 95 index = len; 96 while ( index >= 0 ) { 97 $tr = $rows.eq( index ); 98 mathAbove = $tr.children().filter( mathAttrs[0] ).length; 99 if ( hasFilter ) { 100 $tr = $tr.filter( hasFilter ); 101 } 102 $t = $tr.children().filter( function() { 103 return math.getCellIndex( $( this ) ) === cIndex; 104 }); 105 // ignore filtered rows & rows with data-math="ignore" (and starting row) 106 if ( ( ( hasFilter || !$tr.hasClass( filtered ) ) && 107 $tr.not( mathIgnore ).length && 108 index !== len ) || 109 mathAbove && index !== len ) { 110 // stop calculating 'above', when encountering another 'above' 111 if ( mathAbove ) { 112 index = 0; 113 } else if ( $t.length && $t.not( mathIgnore ).length ) { 114 arry[ arry.length ] = math.processText( c, $t ); 115 } 116 } 117 index--; 118 } 119 } else if ( type === 'below' ) { 120 len = $rows.length; 121 // index + 1 to ignore starting node 122 for ( index = $rows.index( $row ) + 1; index < len; index++ ) { 123 $tr = $rows.eq( index ); 124 if ( $tr.children().filter( mathAttrs[1] ).length ) { 125 break; 126 } 127 if ( hasFilter ) { 128 $tr = $tr.filter( hasFilter ); 129 } 130 $t = $tr.children().filter( function() {
131 return math.getCellIndex( $( this ) ) === cIndex; 132 }); 133 if ( ( hasFilter || !$tr.hasClass( filtered ) ) && 134 $tr.not( mathIgnore ).length && 135 $t.length && $t.not( mathIgnore ) ) { 136 arry[ arry.length ] = math.processText( c, $t ); 137 } 138 } 139 } else { 140 $mathRows = $rows.not( mathIgnore ); 141 len = $mathRows.length; 142 for ( index = 0; index < len; index++ ) { 143 $tr = $mathRows.eq( index ); 144 if ( hasFilter ) { 145 $tr = $tr.filter( hasFilter ); 146 } 147 $t = $tr.children().filter( function() { 148 return math.getCellIndex( $( this ) ) === cIndex; 149 }); 150 if ( ( hasFilter || !$tr.hasClass( filtered ) ) && 151 $t.not( mathAttrs.join( ',' ) ).length && 152 !$t.is( $el ) && $t.not( mathIgnore ).length 153 ) { 154 arry[ arry.length ] = math.processText( c, $t ); 155 } 156 } 157 } 158 return arry; 159 }, 160 161 // get all of the column numerical values in an arry 162 getAll : function( c, hasFilter ) { 163 var $t, col, $row, rowIndex, rowLen, $cells, cellIndex, cellLen, 164 arry = [], 165 wo = c.widgetOptions, 166 mathAttr = wo.math_dataAttrib, 167 mathIgnore = '[' + mathAttr + '=ignore]', 168 filtered = wo.filter_filteredRow || 'filtered', 169 $rows = c.$table.children( 'tbody' ).children().not( mathIgnore ); 170 rowLen = $rows.length; 171 for ( rowIndex = 0; rowIndex < rowLen; rowIndex++ ) { 172 $row = $rows.eq( rowIndex ); 173 if ( hasFilter ) { 174 $row = $row.filter( hasFilter ); 175 } 176 if ( hasFilter || !$row.hasClass( filtered ) ) { 177 $cells = $row.children().not( mathIgnore ); 178 cellLen = $cells.length; 179 for ( cellIndex = 0; cellIndex < cellLen; cellIndex++ ) { 180 $t = $cells.eq( cellIndex ); 181 col = math.getCellIndex( $t ); 182 if ( !$t.filter( '[' + mathAttr + ']' ).length && $.inArray( col, wo.math_ignore ) < 0 ) { 183 arry[ arry.length ] = math.processText( c, $t ); 184 } 185 } 186 } 187 } 188 return arry; 189 }, 190 191 setColumnIndexes : function( c ) { 192 var $table = c.$table, 193 last = 1, 194 // only target rows with a colspan or rows included in a rowspan 195 $rows = $table.children( 'tbody' ).children().filter( function() { 196 var cells, indx, 197 $this = $( this ), 198 include = $this.children( '[colspan]' ).length > 0; 199 if ( last > 1 ) { 200 last--; 201 include = true; 202 } else if ( last < 1 ) { 203 last = 1; 204 } 205 if ( $this.children( '[rowspan]' ).length > 0 ) { 206 cells = this.cells; 207 // find max rowspan (in case more than one cell has a rowspan) 208 for ( indx = 0; indx < cells.length; indx++ ) { 209 last = Math.max( cells[ indx ].rowSpan, last ); 210 } 211 } 212 return include; 213 }); 214 // pass `c` (table.config) to computeColumnIndex so it won't add a data-column 215 // to every tbody cell, just the ones where the .cellIndex property doesn't match 216 // the calculated cell index - hopefully fixes the lag issue in #1048 217 ts.computeColumnIndex( $rows, c ); 218 }, 219 220 getCellIndex : function( $cell ) { 221 var indx = $cell.attr( 'data-column' ); 222 return typeof indx === 'undefined' ? $cell[0].cellIndex : parseInt( indx, 10 ); 223 }, 224 225 recalculate : function(c, wo, init) { 226 if ( c && ( !wo.math_isUpdating || init ) ) { 227 228 var time, mathAttr, $mathCells, indx, len, 229 changed = false, 230 filters = {}; 231 if ( c.debug || wo.math_debug ) { 232 time = new Date(); 233 } 234 235 // add data-column attributes to all table cells 236 if ( init ) { 237 math.setColumnIndexes( c ) ; 238 } 239 240 // data-attribute name (defaults to data-math) 241 wo.math_dataAttrib = 'data-' + (wo.math_data || 'math'); 242 243 // all non-info tbody cells 244 mathAttr = wo.math_dataAttrib; 245 $mathCells = c.$tbodies.children( 'tr' ).children( '[' + mathAttr + ']' ); 246 changed = math.mathType( c, $mathCells, wo.math_priority ) || changed; 247 // only info tbody cells 248 $mathCells = c.$table 249 .children( '.' + c.cssInfoBlock + ', tfoot' ) 250 .children( 'tr' ) 251 .children( '[' + mathAttr + ']' ); 252 math.mathType( c, $mathCells, wo.math_priority ); 253 254 // find the 'all' total 255 $mathCells = c.$table.children().children( 'tr' ).children( '[' + mathAttr + '^=all]' ); 256 len = $mathCells.length; 257 // get math filter, if any 258 // hasFilter = $row.attr( mathAttr + '-filter' ) || wo.math_rowFilter; 259 for (indx = 0; indx < len; indx++) { 260 var $cell = $mathCells.eq( indx ), 261 filter = $cell.attr( mathAttr + '-filter' ) || wo.math_rowFilter; 262 filters[ filter ] = filters[ filter ] ? filters[ filter ].add( $cell ) : $cell; 263 } 264 $.each( filters, function( hasFilter, $cells ) { 265 changed = math.mathType( c, $cells, [ 'all' ], hasFilter ) || changed; 266 }); 267 268 // trigger an update only if cells inside the tbody changed 269 if ( changed ) { 270 wo.math_isUpdating = true; 271 if ( c.debug || wo.math_debug ) { 272 console[ console.group ? 'group' : 'log' ]( 'Math widget updating the cache after recalculation' ); 273 } 274
275 // update internal cache, but ignore "remove-me" rows and do not resort 276 ts.updateCache( c, function() { 277 math.updateComplete( c ); 278 if ( !init && typeof wo.math_completed === 'function' ) { 279 wo.math_completed( c ); 280 } 281 if ( c.debug || wo.math_debug ) { 282 console.log( 'Math widget update completed' + ts.benchmark( time ) ); 283 } 284 }); 285 } else { 286 if ( !init && typeof wo.math_completed === 'function' ) { 287 wo.math_completed( c ); 288 } 289 if ( c.debug || wo.math_debug ) { 290 console.log( 'Math widget found no changes in data' + ts.benchmark( time ) ); 291 } 292 } 293 } 294 }, 295 296 updateComplete : function( c ) { 297 var wo = c.widgetOptions; 298 if ( wo.math_isUpdating && (c.debug || wo.math_debug ) && console.groupEnd ) { 299 console.groupEnd(); 300 } 301 wo.math_isUpdating = false; 302 }, 303 304 mathType : function( c, $cells, priority, hasFilter ) { 305 if ( $cells.length ) { 306 var getAll, 307 changed = false, 308 wo = c.widgetOptions, 309 mathAttr = wo.math_dataAttrib, 310 equations = ts.equations; 311 if ( priority[0] === 'all' ) { 312 // mathType is called multiple times if more than one "hasFilter" is used 313 getAll = math.getAll( c, hasFilter ); 314 } 315 if (c.debug || wo.math_debug) { 316 console[ console.group ? 'group' : 'log' ]( 'Tablesorter Math widget recalculation' ); 317 } 318 // $.each is okay here... only 4 priorities 319 $.each( priority, function( i, type ) { 320 var index, arry, formula, result, $el, 321 $targetCells = $cells.filter( '[' + mathAttr + '^=' + type + ']' ), 322 len = $targetCells.length; 323 if ( len ) { 324 if (c.debug || wo.math_debug) { 325 console[ console.group ? 'group' : 'log' ]( type ); 326 } 327 for ( index = 0; index < len; index++ ) { 328 $el = $targetCells.eq( index ); 329 // Row is filtered, no need to do further checking 330 if ( $el.parent().hasClass( wo.filter_filteredRow || 'filtered' ) ) { 331 continue; 332 } 333 hasFilter = $el.attr( mathAttr + '-filter' ) || wo.math_rowFilter; 334 formula = ( $el.attr( mathAttr ) || '' ).replace( type + '-', '' ); 335 arry = ( type === 'row' ) ? math.getRow( c, $el, hasFilter ) : 336 ( type === 'all' ) ? getAll : math.getColumn( c, $el, type, hasFilter ); 337 if ( equations[ formula ] ) { 338 if ( arry.length ) { 339 result = equations[ formula ]( arry, c ); 340 if ( c.debug || wo.math_debug ) { 341 console.log( $el.attr( mathAttr ), hasFilter ? '("' + hasFilter + '")' : '', arry, '=', result ); 342 } 343 } else { 344 // mean will return a divide by zero error, everything else shows an undefined error 345 result = math.invalid( c, formula, formula === 'mean' ? 0 : 'undef' ); 346 } 347 changed = math.output( $el, c, result, arry ) || changed; 348 } 349 } 350 if ( ( c.debug || wo.math_debug ) && console.groupEnd ) { console.groupEnd(); } 351 } 352 }); 353 if ( ( c.debug || wo.math_debug ) && console.groupEnd ) { console.groupEnd(); } 354 return changed; 355 } 356 return false; 357 }, 358 359 output : function( $cell, c, value, arry ) { 360 // get mask from cell data-attribute: data-math-mask="#,##0.00" 361 var $el, 362 wo = c.widgetOptions, 363 changed = false, 364 prev = $cell.html(), 365 mask = $cell.attr( 'data-' + wo.math_data + '-mask' ) || wo.math_mask, 366 target = $cell.attr( 'data-' + wo.math_data + '-target' ) || '', 367 result = ts.formatMask( mask, value, wo.math_prefix, wo.math_suffix ); 368 if (target) { 369 $el = $cell.find(target); 370 if ($el.length) { 371 $cell = $el; 372 } 373 } 374 if ( typeof wo.math_complete === 'function' ) { 375 result = wo.math_complete( $cell, wo, result, value, arry ); 376 } 377 if ( result !== false ) { 378 changed = prev !== result; 379 $cell.html( result ); 380 } 381 // check if in a regular tbody, otherwise don't pass a changed flag 382 // to prevent unnecessary updating of the table cache 383 if ( changed ) { 384 $el = $cell.closest( 'tbody' ); 385 // content was changed in a tfoot, info-only tbody or the resulting tbody is in a nested table 386 // then don't signal a change 387 if ( !$el.length || $el.hasClass( c.cssInfoBlock ) || $el.parent()[0] !== c.table ) { 388 return false;
389 } 390 } 391 return changed; 392 } 393 394 }; 395 396 // Modified from https://code.google.com/p/javascript-number-formatter/ 397 /** 398 * @preserve IntegraXor Web SCADA - JavaScript Number Formatter 399 * http:// www.integraxor.com/ 400 * author: KPL, KHL 401 * (c)2011 ecava 402 * Dual licensed under the MIT or GPL Version 2 licenses. 403 */ 404 ts.formatMask = function( mask, val, tmpPrefix, tmpSuffix ) { 405 if ( !mask || isNaN( +val ) ) { 406 return val; // return as it is. 407 } 408 409 var isNegative, result, decimal, group, posLeadZero, posTrailZero, posSeparator, part, szSep, 410 integer, str, offset, index, end, inv, 411 suffix = '', 412 413 // find prefix/suffix 414 len = mask.length, 415 start = mask.search( /[0-9\-\+#]/ ), 416 tmp = start > 0 ? mask.substring( 0, start ) : '', 417 prefix = tmp; 418 if ( tmpPrefix ) { 419 if ( /\{content\}/.test( tmpPrefix || '' ) ) { 420 prefix = ( tmpPrefix || '' ).replace( /\{content\}/g, tmp || '' ); 421 } else { 422 prefix = ( tmpPrefix || '' ) + tmp; 423 } 424 } 425 // reverse string: not an ideal method if there are surrogate pairs 426 inv = mask.split( '' ).reverse().join( '' ); 427 end = inv.search( /[0-9\-\+#]/ ); 428 index = len - end; 429 index += ( mask.substring( index, index + 1 ) === '.' ) ? 1 : 0; 430 tmp = end > 0 ? mask.substring( index, len ) : ''; 431 suffix = tmp; 432 if ( tmpSuffix ) { 433 if ( /\{content\}/.test( tmpSuffix || '' ) ) { 434 suffix = ( tmpSuffix || '' ).replace( /\{content\}/g, tmp || '' ); 435 } else { 436 suffix = tmp + ( tmpSuffix || '' ); 437 } 438 } 439 mask = mask.substring( start, index ); 440 441 // convert any string to number according to formation sign. 442 val = mask.charAt( 0 ) === '-' ? -val : +val; 443 isNegative = val < 0 ? val = -val : 0; // process only abs(), and turn on flag. 444 445 // search for separator for grp & decimal, anything not digit, not +/- sign, not #. 446 result = mask.match( /[^\d\-\+#]/g ); 447 decimal = ( result && result[ result.length - 1 ] ) || '.'; // treat the right most symbol as decimal 448 group = ( result && result[ 1 ] && result[ 0 ] ) || ','; // treat the left most symbol as group separator 449 450 // split the decimal for the format string if any. 451 mask = mask.split( decimal ); 452 // Fix the decimal first, toFixed will auto fill trailing zero. 453 val = val.toFixed( mask[ 1 ] && mask[ 1 ].length ); 454 val = +( val ) + ''; // convert number to string to trim off *all* trailing decimal zero(es) 455 456 // fill back any trailing zero according to format 457 posTrailZero = mask[ 1 ] && mask[ 1 ].lastIndexOf( '0' ); // look for last zero in format 458 part = val.split( '.' ); 459 // integer will get !part[1] 460 if ( !part[ 1 ] || ( part[ 1 ] && part[ 1 ].length <= posTrailZero ) ) { 461 val = ( +val ).toFixed( posTrailZero + 1 ); 462 } 463 szSep = mask[ 0 ].split( group ); // look for separator 464 mask[ 0 ] = szSep.join( '' ); // join back without separator for counting the pos of any leading 0. 465 466 posLeadZero = mask[ 0 ] && mask[ 0 ].indexOf( '0' ); 467 if ( posLeadZero > -1 ) { 468 while ( part[ 0 ].length < ( mask[ 0 ].length - posLeadZero ) ) { 469 part[ 0 ] = '0' + part[ 0 ]; 470 } 471 } else if ( +part[ 0 ] === 0 ) { 472 part[ 0 ] = ''; 473 } 474 475 val = val.split( '.' ); 476 val[ 0 ] = part[ 0 ]; 477 478 // process the first group separator from decimal (.) only, the rest ignore. 479 // get the length of the last slice of split result. 480 posSeparator = ( szSep[ 1 ] && szSep[ szSep.length - 1 ].length ); 481 if ( posSeparator ) { 482 integer = val[ 0 ]; 483 str = ''; 484 offset = integer.length % posSeparator; 485 len = integer.length; 486 for ( index = 0; index < len; index++ ) { 487 str += integer.charAt( index ); // ie6 only support charAt for sz. 488 // -posSeparator so that won't trail separator on full length 489 /*jshint -W018 */ 490 if ( !( ( index - offset + 1 ) % posSeparator ) && index < len - posSeparator ) { 491 str += group; 492 } 493 } 494 val[ 0 ] = str; 495 } 496 497 val[ 1 ] = ( mask[ 1 ] && val[ 1 ] ) ? decimal + val[ 1 ] : ''; 498 // put back any negation, combine integer and fraction, and add back prefix & suffix 499 return prefix + ( ( isNegative ? '-' : '' ) + val[ 0 ] + val[ 1 ] ) + suffix; 500 }; 501 502 ts.equations = { 503 count : function( arry ) { 504 return arry.length; 505 }, 506 sum : function( arry ) { 507 var index, 508 len = arry.length, 509 total = 0; 510 for ( index = 0; index < len; index++ ) { 511 total += arry[ index ]; 512 } 513 return total; 514 }, 515 mean : function( arry ) { 516 var total = ts.equations.sum( arry ); 517 return total / arry.length; 518 }, 519 median : function( arry, c ) { 520 var half, 521 len = arry.length; 522 if ( len > 1 ) { 523 // https://gist.github.com/caseyjustus/1166258 524 arry.sort( function( a, b ) { return a - b; } ); 525 half = Math.floor( len / 2 ); 526 return ( len % 2 ) ? arry[ half ] : ( arry[ half - 1 ] + arry[ half ] ) / 2; 527 } 528 return math.invalid( c, 'median', 1 ); 529 }, 530 mode : function( arry ) { 531 // http://stackoverflow.com/a/3451640/145346 532 var index, el, m, 533 modeMap = {}, 534 maxCount = 1, 535 modes = [ arry[ 0 ] ]; 536 for ( index = 0; index < arry.length; index++ ) { 537 el = arry[ index ]; 538 modeMap[ el ] = modeMap[ el ] ? modeMap[ el ] + 1 : 1; 539 m = modeMap[ el ]; 540 if ( m > maxCount ) { 541 modes = [ el ]; 542 maxCount = m; 543 } else if ( m === maxCount ) { 544 modes[ modes.length ] = el; 545 maxCount = m; 546 } 547 } 548 // returns arry of modes if there is a tie 549 return modes.sort( function( a, b ) { return a - b; } ); 550 }, 551 max : function(arry) { 552 return Math.max.apply( Math, arry ); 553 }, 554 min : function(arry) { 555 return Math.min.apply( Math, arry ); 556 }, 557 range: function(arry) { 558 var v = arry.sort( function( a, b ) { return a - b; } ); 559 return v[ arry.length - 1 ] - v[ 0 ]; 560 }, 561 // common variance equation 562 // (not accessible via data-attribute setting) 563 variance: function( arry, population, c ) { 564 var divisor, 565 avg = ts.equations.mean( arry ), 566 v = 0, 567 i = arry.length; 568 while ( i-- ) { 569 v += Math.pow( ( arry[ i ] - avg ), 2 ); 570 } 571 divisor = ( arry.length - ( population ? 0 : 1 ) ); 572 if ( divisor === 0 ) { 573 return math.invalid( c, 'variance', 0 ); 574 } 575 v /= divisor; 576 return v; 577 }, 578 // variance (population) 579 varp : function( arry, c ) { 580 return ts.equations.variance( arry, true, c ); 581 }, 582 // variance (sample) 583 vars : function( arry, c ) { 584 return ts.equations.variance( arry, false, c ); 585 }, 586 // standard deviation (sample) 587 stdevs : function( arry, c ) { 588 var vars = ts.equations.variance( arry, false, c ); 589 return Math.sqrt( vars ); 590 }, 591 // standard deviation (population) 592 stdevp : function( arry, c ) { 593 var varp = ts.equations.variance( arry, true, c ); 594 return Math.sqrt( varp ); 595 } 596 }; 597 598 // add new widget called repeatHeaders 599 // ************************************ 600 ts.addWidget({ 601 id: 'math', 602 priority: 100, 603 options: { 604 math_data : 'math', 605 math_debug : false, 606 // column index to ignore 607 math_ignore : [], 608 // mask info: https://code.google.com/p/javascript-number-formatter/ 609 math_mask : '#,##0.00', 610 // complete executed after each fucntion 611 math_complete : null, // function($cell, wo, result, value, arry) { return result; }, 612 // math_completed called after all math calculations have completed 613 math_completed: function( /* config */ ) {}, 614 // order of calculation; 'all' is last 615 math_priority : [ 'row', 'above', 'below', 'col' ], 616 // template for or just prepend the mask prefix & suffix with this HTML 617 // e.g. '<span class="red">{content}</span>' 618 math_prefix : '', 619 math_suffix : '', 620 // cell attribute containing the math value to use 621 math_textAttr : '', 622 // no matching math elements found (text added to cell) 623 math_none : 'N/A', 624 math_event : 'recalculate', 625 // use this filter to target specific rows (e.g. ':visible', or ':not(.empty-row)') 626 math_rowFilter: '' 627 }, 628 init : function( table, thisWidget, c, wo ) { 629 // filterEnd fires after updateComplete 630 var update = ( ts.hasWidget( table, 'filter' ) ? 'filterEnd' : 'updateComplete' ) + '.tsmath'; 631 // filterEnd is when the pager hides rows... so bind to pagerComplete
632 math.events += ( ts.hasWidget( table, 'pager' ) ? 'pagerComplete' : 'filterEnd' ) + '.tsmath '; 633 c.$table 634 .off( ( math.events + 'updateComplete.tsmath ' + wo.math_event ).replace( /\s+/g, ' ' ) ) 635 .on( math.events + wo.math_event, function( e ) { 636 if ( !this.hasInitialized ) { return; } 637 var init = e.type === 'tablesorter-initialized'; 638 if ( !wo.math_isUpdating || init ) { 639 // don't setColumnIndexes on init here, or it gets done twice 640 if ( !/filter/.test( e.type ) && !init ) { 641 // redo data-column indexes on update 642 math.setColumnIndexes( c ); 643 } 644 math.recalculate( c, wo, init ); 645 } 646 }) 647 .on( update, function() { 648 setTimeout( function() { 649 math.updateComplete( c ); 650 }, 40 ); 651 }); 652 wo.math_isUpdating = false; 653 // math widget initialized after table - see #946 654 if ( table.hasInitialized ) { 655 math.recalculate( c, wo, true ); 656 } 657 }, 658 // this remove function is called when using the refreshWidgets method or when destroying the tablesorter plugin 659 // this function only applies to tablesorter v2.4+ 660 remove: function( table, c, wo, refreshing ) { 661 if ( refreshing ) { return; } 662 c.$table 663 .off( ( math.events + ' updateComplete.tsmath ' + wo.math_event ).replace( /\s+/g, ' ' ) ) 664 .children().children( 'tr' ).children( '[data-' + wo.math_data + ']' ).empty(); 665 } 666 }); 667 668})(jQuery);
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.