bgalaz / jquery-jtps

Automatically exported from code.google.com/p/jquery-jtps
0 stars 0 forks source link

Patch for /trunk/jTPS.js #12

Open GoogleCodeExporter opened 9 years ago

GoogleCodeExporter commented 9 years ago
/*
 * jTPS - table sorting, pagination, and animated page scrolling
 *  version 0.5.1
 * Author: Jim Palmer
 * Released under MIT license.
 */
 (function($) {

  // apply table controls + setup initial jTPS namespace within jQuery
  $.fn.jTPS = function ( opt ) {

    $(this).data('tableSettings', $.extend({
      perPages:      [5, 6, 10, 20, 50, 'ALL'],      
  // the "show per page" selection
      perPageText:    'Show per page:',          
  // text that appears before perPages links
      perPageDelim:    '<span style="color:#ccc;">|</span>',  
// text or dom node that deliminates each perPage link 
      perPageSeperator:  '..',              
    // text or dom node that deliminates split in select page links
      scrollDelay:    30,                
    // delay (in ms) between steps in anim. - IE has trouble showing 
animation with < 30ms delay
      scrollStep:      2,              
      // how many tr's are scrolled per step in the animated vertical 
pagination scrolling
      fixedLayout:    true,              
    // autoset the width/height on each cell and set table-layout to 
fixed after auto layout
      clickCallback:    function () {}          
    // callback function after clicks on sort, perpage and pagination
    }, opt));
    
    // generic pass-through object + other initial variables
    var pT = $(this), page = page || 1, perPages = 
$(this).data('tableSettings').perPages, perPage = perPage || perPages[0],
      rowCount = $('>tbody', this).find('tr').length;

    // append jTPS class "stamp"
    $(this).addClass('jTPS');
    
    // setup the fixed table-layout so that the animation doesn't bounce 
around - faux grid for table
    if ( $(this).data('tableSettings').fixedLayout ) {
      // "fix" the table layout and individual cell width & height 
settings
      if ( $(this).css('table-layout') != 'fixed' ) {
        // find max tbody td cell height
        var maxCellHeight = 0;

        // set width style on the TH headers (rely on jQuery with 
computed styles support)
        $('>thead', this).find('th,td').each(function () { 
$(this).css('width', $(this).width()); });

        // ensure browser-formated widths for each column in the 
thead and tbody
        var tbodyCh = $('>tbody',this)[0].childNodes, tmpp = 0;
        // loop through tbody children and find the Nth <TR>
        for ( var tbi=0, tbcl=tbodyCh.length; tbi < tbcl; tbi++ )
          if ( tbodyCh[ tbi ].nodeName == 'TR' )
            maxCellHeight = Math.max( maxCellHeight, tbodyCh[ 
tbi ].offsetHeight );

        // now set the height attribute and/or style to the first 
TD cell (not the row)
        for ( var tbi=0, tbcl=tbodyCh.length; tbi < tbcl; tbi++ )
          if ( tbodyCh[ tbi ].nodeName == 'TR' )
            for ( var tdi=0, trCh=tbodyCh[ tbi ].childNodes, 
tdcl=trCh.length; tdi < tdcl; tdi++ )
              if ( trCh[ tdi ].nodeName == 'TD' ) {
                trCh[ tdi ].style.height = 
maxCellHeight + 'px';
                tdi = tdcl;
              }
        // now set the table layout to fixed
        $(this).css('table-layout','fixed');
      }
    }

    // remove all stub rows
    $('.stubCell', this).remove();

    // add the stub rows
    var stubCount=0, cols = Math.max( $('>thead:first tr:last 
th,>thead:first tr:last td', this).length, parseInt( $('>thead:first tr:last 
th,>thead:first tr:last td').attr('colspan') || 0 ) ), 
      stubs = ( perPage - ( $('>tbody>tr', this).length % perPage ) ),
      stubHeight = $('>tbody>tr:first>td:first', this).css('height');
    for ( ; stubCount < stubs && stubs != perPage; stubCount++ )
      $('>tbody>tr:last', this).after( '<tr class="stubCell"><td 
colspan="' + cols + '" style="height: ' + stubHeight + ';">&nbsp;</td></tr>' );

    // paginate the result
    if ( rowCount > perPage && perPage != 0 )
      $('>tbody>tr:gt(' + (perPage - 1) + ')', 
this).addClass('hideTR');

    // bind sort functionality to theader
    if (perPage != 0)
      $('>thead [sort],>thead .sort', this).each(
        function (tdInd) {
          
$(this).addClass('sortableHeader').unbind('click').bind('click',
            function () {
              var columnNo = $('>thead tr:last', 
pT).children().index( $(this) ),
                desc = $('>thead [sort],>thead .sort', 
pT).eq(columnNo).hasClass('sortAsc') ? true : false;
              // sort the rows
              sort( pT, columnNo, desc );
              // show first perPages rows
              var page = parseInt( 
$('.hilightPageSelector:first', pT).html() ) || 1;
              $('>tbody>tr', 
pT).removeClass('hideTR').filter(':gt(' + ( ( perPage - 1 ) * page ) + 
')').addClass('hideTR');
              $('>tbody>tr:lt(' + ( ( perPage - 1 ) * ( 
page - 1 ) ) + ')', pT).addClass('hideTR');
              // scroll to first page if not already
              if ($('.pageSelector', 
pT).index($('.hilightPageSelector', pT)) > 0)
                $('.pageSelector:first', pT).click();
              // hilight the sorted column header
              $('>thead .sortDesc,>thead .sortAsc', 
pT).removeClass('sortDesc').removeClass('sortAsc');
              $('>thead [sort],>thead .sort', 
pT).eq(columnNo).addClass( desc ? 'sortDesc' : 'sortAsc' );
              // hilight the sorted column
              $('>tbody>tr>td.sortedColumn', 
pT).removeClass('sortedColumn');
              $('>tbody>tr:not(.stubCell)', pT).each( 
function () { $('>td:eq(' + columnNo + ')', this).addClass('sortedColumn'); } );
              clearSelection();
              // callback function after pagination renderd
              $(pT).data('tableSettings').clickCallback();
            }
          );
        }
      );

    // add perPage selection link + delim dom node
    $('>.nav .selectPerPage', this).empty();
    var pageSel = perPages.length;
    while ( pageSel-- ) 
      $('>.nav .selectPerPage', this).prepend( ( (pageSel > 0) ? 
$(this).data('tableSettings').perPageDelim : '' ) + 
        '<span class="perPageSelector">' + perPages[pageSel] + 
'</span>' );

    // now draw the page selectors
    drawPageSelectors( this, page || 1 );

    // prepend the instructions and attach select hover and click events
    $('>.nav .selectPerPage', this).prepend( 
$(this).data('tableSettings').perPageText ).find('.perPageSelector').each(
      function () {
        if ( ( parseInt($(this).html()) || rowCount ) == perPage )
          $(this).addClass('perPageSelected');
        $(this).bind('mouseover mouseout', 
          function (e) { 
            e.type == 'mouseover' ? 
$(this).addClass('perPageHilight') : $(this).removeClass('perPageHilight');
          }
        );
        $(this).bind('click', 
          function () { 
            // set the new number of pages
            perPage = parseInt( $(this).html() ) || rowCount;
            if ( perPage > rowCount ) perPage = rowCount;
            // remove all stub rows
            $('.stubCell', this).remove();
            // redraw stub rows
            var stubCount=0, cols = $('>thead th,>thead td', 
pT).length, 
              stubs = ( perPage - ( $('>tbody>tr', 
pT).length % perPage ) ), 
              stubHeight = $('>tbody>tr:first>td:first', 
pT).css('height');
            for ( ; stubCount < stubs && stubs != perPage; 
stubCount++ )
              $('>tbody>tr:last', pT).after( '<tr 
class="stubCell"><td colspan="' + cols + '" style="height: ' + stubHeight + 
';">&nbsp;</td></tr>' );
            // set new visible rows
            $('>tbody>tr', 
pT).removeClass('hideTR').filter(':gt(' + ( ( perPage - 1 ) * page ) + 
')').addClass('hideTR');
            $('>tbody>tr:lt(' + ( ( perPage - 1 ) * ( page - 
1 ) ) + ')', pT).addClass('hideTR');
            // back to the first page
            $('.pageSelector:first', pT).click();
            
$(this).siblings('.perPageSelected').removeClass('perPageSelected');
            $(this).addClass('perPageSelected');
            // redraw the pagination
            drawPageSelectors( pT, 1 );
            // update status bar
            var cPos = $('>tbody>tr:not(.hideTR):first', 
pT).prevAll().length,
              ePos = 
$('>tbody>tr:not(.hideTR):not(.stubCell)', pT).length;
            $('>.nav .status', pT).html( 'Showing ' + ( cPos 
+ 1 ) + ' - ' + ( cPos + ePos ) + ' of ' + rowCount + '' );
            clearSelection();
            // callback function after pagination renderd
            $(pT).data('tableSettings').clickCallback();
          }
        );
      }
    );
    
    // show the correct paging status
    var cPos = $('>tbody>tr:not(.hideTR):first', this).prevAll().length, 
      ePos = $('>tbody>tr:not(.hideTR):not(.stubCell)', this).length;
    $('>.nav .status', this).html( 'showing ' + ( cPos + 1 ) + ' - ' + ( 
cPos + ePos ) + ' of ' + rowCount );

    // clear selected text function
    function clearSelection () {
      if ( document.selection && typeof(document.selection.empty) != 
'undefined' )
        document.selection.empty();
      else if ( typeof(window.getSelection) === 'function' && 
typeof(window.getSelection().removeAllRanges) === 'function' )
        window.getSelection().removeAllRanges();
    }

    // render the pagination functionality
    function drawPageSelectors ( target, page ) {

      // add pagination links
      $('>.nav .pagination', target).empty();
      var pages = ( perPage >= rowCount || perPage == 0 ) ? 0 : 
Math.ceil( rowCount / perPage ), totalPages = pages;
      while ( pages-- )
        $('>.nav .pagination', target).prepend( '<div 
class="pageSelector">' + ( pages + 1 ) + '</div>' );
      var pageCount = $('>.nav:first .pageSelector', target).length;
      $('>.nav', target).each(function () {
        $('.hidePageSelector', 
this).removeClass('hidePageSelector');
        $('.hilightPageSelector', 
this).removeClass('hilightPageSelector');
        $('.pageSelectorSeperator', this).remove();
        $('.pageSelector:lt(' + ( ( page > ( pageCount - 4 ) ) ? ( 
pageCount - 5 ) : ( page - 2 ) ) + '):not(:first)', 
this).addClass('hidePageSelector')
          .eq(0).after( '<div class="pageSelectorSeperator">' + 
$(target).data('tableSettings').perPageSeperator + '</div>' );
        $('.pageSelector:gt(' + ( ( page < 4 ) ? 4 : page ) + 
'):not(:last)', this).addClass('hidePageSelector')
          .eq(0).after( '<div class="pageSelectorSeperator">' + 
$(target).data('tableSettings').perPageSeperator + '</div>' );
        $('.pageSelector:eq(' + ( page - 1 ) + ')', 
this).addClass('hilightPageSelector');
      });

      // remove the pager title if no pages necessary
      if ( perPage >= rowCount )
        $('>.nav .paginationTitle', target).css('display','none');
      else
        $('>.nav .paginationTitle', target).css('display','');
      
      // bind the pagination onclick
      $('>.nav .pagination .pageSelector', target).each(
        function () {
          $(this).bind('click',
            function () {

              // if double clicked - stop animation and 
jump to selected page - this appears to be a tripple click in IE7
              if ( $(this).hasClass('hilightPageSelector') 
) {
                if ( $(this).parent().queue().length > 
0 ) {
                  // really stop all animations and 
create new queue
                  $(this).parent().stop().queue( 
"fx", [] ).stop();
                  // set the user directly on the 
correct page without animation
                  var beginPos = ( ( parseInt( 
$(this).html() ) - 1 ) * perPage ), endPos = beginPos + perPage;
                  $('>tbody> tr', 
pT).removeClass('hideTR').addClass('hideTR');
                  $('>tbody>tr:gt(' + (beginPos - 2) 
+ '):lt(' + ( perPage ) + ')', pT).andSelf().removeClass('hideTR');
                  // update status bar
                  var cPos = 
$('>tbody>tr:not(.hideTR):first', pT).prevAll().length,
                    ePos = 
$('>tbody>tr:not(.hideTR):not(.stubCell)', pT).length;
                  $('>.nav .status', pT).html( 
'Showing ' + ( cPos + 1 ) + ' - ' + ( cPos + ePos ) + ' of ' + rowCount + '' );
                }
                clearSelection();
                return false;
              }

              // hilight the specific page button
              $(this).addClass('hilightPageSelector');

              // really stop all animations
              $(this).parent().stop().queue( "fx", [] 
).stop().dequeue();

              // setup the pagination variables
              var beginPos = 
$('>tbody>tr:not(.hideTR):first', pT).prevAll().length,
                endPos = ( ( parseInt( $(this).html() ) 
- 1 ) * perPage );
              if ( endPos > rowCount )
                endPos = (rowCount - 1);
              // set the steps to be exponential for all 
the page scroll difference - i.e. faster for more pages to scroll
              var sStep = 
$(pT).data('tableSettings').scrollStep * Math.ceil( Math.abs( ( endPos - 
beginPos ) / perPage ) );
              if ( sStep > perPage ) sStep = perPage;
              var steps = Math.ceil( Math.abs( beginPos - 
endPos ) / sStep );

              // start scrolling
              while ( steps-- ) {
                $(this).parent().animate({'opacity':1}, 
$(pT).data('tableSettings').scrollDelay,
                  function () {
                    // reset the scrollStep for 
the remaining items
                    if ( 
$(this).queue("fx").length == 0 )
                      sStep = ( Math.abs( 
beginPos - endPos ) % sStep ) || sStep;
                    /* scoll up */
                    if ( beginPos > endPos ) {
                      
$('>tbody>tr:not(.hideTR):first', pT).prevAll(':lt(' + sStep + 
')').removeClass('hideTR');
                      if ( 
$('>tbody>tr:not(.hideTR)', pT).length > perPage )
                        
$('>tbody>tr:not(.hideTR):last', pT).prevAll(':lt(' + ( sStep - 1 ) + 
')').andSelf().addClass('hideTR');
                      // if scrolling up from 
less rows than perPage - compensate if < perPage
                      var currRows =  
$('>tbody>tr:not(.hideTR)', pT).length;
                      if ( currRows < perPage )
                        
$('>tbody>tr:not(.hideTR):last', pT).nextAll(':lt(' + ( perPage - currRows ) + 
')').removeClass('hideTR');
                    /* scroll down */
                    } else {
                      var endPoint = 
$('>tbody>tr:not(.hideTR):last', pT);
                      
$('>tbody>tr:not(.hideTR):lt(' + sStep + ')', pT).addClass('hideTR');
                      
$(endPoint).nextAll(':lt(' + sStep + ')').removeClass('hideTR');
                    }
                    // update status bar
                    var cPos = 
$('>tbody>tr:not(.hideTR):first', pT).prevAll().length,
                      ePos = 
$('>tbody>tr:not(.hideTR):not(.stubCell)', pT).length;
                    $('>.nav .status', pT).html( 
'Showing ' + ( cPos + 1 ) + ' - ' + ( cPos + ePos ) + ' of ' + rowCount + '' );
                  }
                );
              }
              
              // redraw the pagination
              drawPageSelectors( pT, parseInt( 
$(this).html() ) );
              
              // callback function after pagination renderd
              $(pT).data('tableSettings').clickCallback();
              
            }
          );
        }
      );
      
    };
    // sort wrapper function
    function sort ( target, tdIndex, desc ) {
      var fCol = $('>thead th,>thead th', target).get(tdIndex),
        sorted = $(fCol).hasClass('sortAsc') || 
$(fCol).hasClass('sortDesc') || false,
        nullChar = String.fromCharCode(0), 
        re = /([-]?[0-9\.]+)/g,
        rows = $('>tbody>tr:not(.stubCell)', target).get(), 
        procRow = [];

      $(rows).each(
        function(key, val) {
          procRow.push( $('>td:eq(' + tdIndex + ')', val).text() 
+ nullChar + procRow.length );
        }
      );
      if ( !sorted ) {
        // natural sort
        procRow.sort(
          function naturalSort (a, b) {
            // setup temp-scope variables for comparison 
evauluation
            var re = /(-?[0-9\.]+)/g,
              nC = String.fromCharCode(0),
              x = a.toString().toLowerCase().split(nC)[0] 
|| '',
              y = b.toString().toLowerCase().split(nC)[0] 
|| '',
              xN = x.replace( re, nC + '$1' + nC 
).split(nC),
              yN = y.replace( re, nC + '$1' + nC 
).split(nC),
              xD = (new Date(x)).getTime(),
              yD = xD ? (new Date(y)).getTime() : null;
            // natural sorting of dates
            if ( yD )
              if ( xD < yD ) return -1;
              else if ( xD > yD )  return 1;
            // natural sorting through split numeric strings 
and default strings
            for( var cLoc = 0, numS = Math.max(xN.length, 
yN.length); cLoc < numS; cLoc++ ) {
              oFxNcL = parseFloat(xN[cLoc]) || xN[cLoc];
              oFyNcL = parseFloat(yN[cLoc]) || yN[cLoc];
              if (oFxNcL < oFyNcL) return -1;
              else if (oFxNcL > oFyNcL) return 1;
            }
            return 0;
          });
        if ( !desc ) procRow.reverse(); // properly position order 
of sort
      }
      // now re-order the parent tbody based off the quick sorted 
tbody map
      $('>tbody', 
target).addClass('jtpstemp').before('<tbody></tbody>');
      var nr = procRow.length, tf = $('>tbody', target)[0];
      // move the row from old tbody to new tbody in order of new 
tbody with replaceWith to retain original tbody row positioning
      if ( sorted )
        while ( nr-- )
          tf.appendChild( rows[ nr ] );
      else
        while ( nr-- )
          tf.appendChild( rows[ parseInt( procRow[ nr 
].split(nullChar).pop() ) ] );
      // remove the old table
      $('>tbody.jtpstemp', target).remove();
      // redraw stub rows
      var stubCount=0, cols = $('>thead>tr:last th', target).length, 
        stubs = ( perPage - ( $('>tbody>tr', target).length % 
perPage ) ), 
        stubHeight = $('>tbody>tr:first>td:first', 
target).css('height');
      for ( ; stubCount < stubs && stubs != perPage; stubCount++ )
        $('>tbody>tr:last', target).after( '<tr 
class="stubCell"><td colspan="' + cols + '" style="height: ' + stubHeight + 
';">&nbsp;</td></tr>' );
    }
    // chainable
    return this;
  };

})(jQuery);

Original issue reported on code.google.com by Telephon...@gmail.com on 20 Aug 2013 at 1:47

Attachments: