/*! Widget: resizable - updated 5/16/2015 (v2.26.1) */ /*jshint browser:true, jquery:true, unused:false */ ;(function ($, window) { 'use strict'; var ts = $.tablesorter || {}; $.extend(ts.css, { resizableContainer : 'tablesorter-resizable-container', resizableHandle : 'tablesorter-resizable-handle', resizableNoSelect : 'tablesorter-disableSelection', resizableStorage : 'tablesorter-resizable' }); // Add extra scroller css $(function(){ var s = ''; $(s).appendTo('body'); }); ts.resizable = { init : function( c, wo ) { if ( c.$table.hasClass( 'hasResizable' ) ) { return; } c.$table.addClass( 'hasResizable' ); var noResize, $header, column, storedSizes, tmp, $table = c.$table, $parent = $table.parent(), marginTop = parseInt( $table.css( 'margin-top' ), 10 ), // internal variables vars = wo.resizable_vars = { useStorage : ts.storage && wo.resizable !== false, $wrap : $parent, mouseXPosition : 0, $target : null, $next : null, overflow : $parent.css('overflow') === 'auto' || $parent.css('overflow') === 'scroll' || $parent.css('overflow-x') === 'auto' || $parent.css('overflow-x') === 'scroll', storedSizes : [] }; // set default widths ts.resizableReset( c.table, true ); // now get measurements! vars.tableWidth = $table.width(); // attempt to autodetect vars.fullWidth = Math.abs( $parent.width() - vars.tableWidth ) < 20; /* // Hacky method to determine if table width is set to 'auto' // http://stackoverflow.com/a/20892048/145346 if ( !vars.fullWidth ) { tmp = $table.width(); $header = $table.wrap('').parent(); // temp variable storedSizes = parseInt( $table.css( 'margin-left' ), 10 ) || 0; $table.css( 'margin-left', storedSizes + 50 ); vars.tableWidth = $header.width() > tmp ? 'auto' : tmp; $table.css( 'margin-left', storedSizes ? storedSizes : '' ); $header = null; $table.unwrap(''); } */ if ( vars.useStorage && vars.overflow ) { // save table width ts.storage( c.table, 'tablesorter-table-original-css-width', vars.tableWidth ); tmp = ts.storage( c.table, 'tablesorter-table-resized-width' ) || 'auto'; ts.resizable.setWidth( $table, tmp, true ); } wo.resizable_vars.storedSizes = storedSizes = ( vars.useStorage ? ts.storage( c.table, ts.css.resizableStorage ) : [] ) || []; ts.resizable.setWidths( c, wo, storedSizes ); ts.resizable.updateStoredSizes( c, wo ); wo.$resizable_container = $( '
' ) .css({ top : marginTop }) .insertBefore( $table ); // add container for ( column = 0; column < c.columns; column++ ) { $header = c.$headerIndexed[ column ]; tmp = ts.getColumnData( c.table, c.headers, column ); noResize = ts.getData( $header, tmp, 'resizable' ) === 'false'; if ( !noResize ) { $( '
' ) .appendTo( wo.$resizable_container ) .attr({ 'data-column' : column, 'unselectable' : 'on' }) .data( 'header', $header ) .bind( 'selectstart', false ); } } ts.resizable.bindings( c, wo ); }, updateStoredSizes : function( c, wo ) { var column, $header, len = c.columns, vars = wo.resizable_vars; vars.storedSizes = []; for ( column = 0; column < len; column++ ) { $header = c.$headerIndexed[ column ]; vars.storedSizes[ column ] = $header.is(':visible') ? $header.width() : 0; } }, setWidth : function( $el, width, overflow ) { // overflow tables need min & max width set as well $el.css({ 'width' : width, 'min-width' : overflow ? width : '', 'max-width' : overflow ? width : '' }); }, setWidths : function( c, wo, storedSizes ) { var column, $temp, vars = wo.resizable_vars, $extra = $( c.namespace + '_extra_headers' ), $col = c.$table.children( 'colgroup' ).children( 'col' ); storedSizes = storedSizes || vars.storedSizes || []; // process only if table ID or url match if ( storedSizes.length ) { for ( column = 0; column < c.columns; column++ ) { // set saved resizable widths ts.resizable.setWidth( c.$headerIndexed[ column ], storedSizes[ column ], vars.overflow ); if ( $extra.length ) { // stickyHeaders needs to modify min & max width as well $temp = $extra.eq( column ).add( $col.eq( column ) ); ts.resizable.setWidth( $temp, storedSizes[ column ], vars.overflow ); } } $temp = $( c.namespace + '_extra_table' ); if ( $temp.length && !ts.hasWidget( c.table, 'scroller' ) ) { ts.resizable.setWidth( $temp, c.$table.outerWidth(), vars.overflow ); } } }, setHandlePosition : function( c, wo ) { var startPosition, tableHeight = c.$table.height(), $handles = wo.$resizable_container.children(), handleCenter = Math.floor( $handles.width() / 2 ); if ( ts.hasWidget( c.table, 'scroller' ) ) { tableHeight = 0; c.$table.closest( '.' + ts.css.scrollerWrap ).children().each(function(){ var $this = $(this); // center table has a max-height set tableHeight += $this.filter('[style*="height"]').length ? $this.height() : $this.children('table').height(); }); } // subtract out table left position from resizable handles. Fixes #864 startPosition = c.$table.position().left; $handles.each( function() { var $this = $(this), column = parseInt( $this.attr( 'data-column' ), 10 ), columns = c.columns - 1, $header = $this.data( 'header' ); if ( !$header ) { return; } // see #859 if ( !$header.is(':visible') ) { $this.hide(); } else if ( column < columns || column === columns && wo.resizable_addLastColumn ) { $this.css({ display: 'inline-block', height : tableHeight, left : $header.position().left - startPosition + $header.outerWidth() - handleCenter }); } }); }, // prevent text selection while dragging resize bar toggleTextSelection : function( c, wo, toggle ) { var namespace = c.namespace + 'tsresize'; wo.resizable_vars.disabled = toggle; $( 'body' ).toggleClass( ts.css.resizableNoSelect, toggle ); if ( toggle ) { $( 'body' ) .attr( 'unselectable', 'on' ) .bind( 'selectstart' + namespace, false ); } else { $( 'body' ) .removeAttr( 'unselectable' ) .unbind( 'selectstart' + namespace ); } }, bindings : function( c, wo ) { var namespace = c.namespace + 'tsresize'; wo.$resizable_container.children().bind( 'mousedown', function( event ) { // save header cell and mouse position var column, vars = wo.resizable_vars, $extras = $( c.namespace + '_extra_headers' ), $header = $( event.target ).data( 'header' ); column = parseInt( $header.attr( 'data-column' ), 10 ); vars.$target = $header = $header.add( $extras.filter('[data-column="' + column + '"]') ); vars.target = column; // if table is not as wide as it's parent, then resize the table vars.$next = event.shiftKey || wo.resizable_targetLast ? $header.parent().children().not( '.resizable-false' ).filter( ':last' ) : $header.nextAll( ':not(.resizable-false)' ).eq( 0 ); column = parseInt( vars.$next.attr( 'data-column' ), 10 ); vars.$next = vars.$next.add( $extras.filter('[data-column="' + column + '"]') ); vars.next = column; vars.mouseXPosition = event.pageX; ts.resizable.updateStoredSizes( c, wo ); ts.resizable.toggleTextSelection(c, wo, true ); }); $( document ) .bind( 'mousemove' + namespace, function( event ) { var vars = wo.resizable_vars; // ignore mousemove if no mousedown if ( !vars.disabled || vars.mouseXPosition === 0 || !vars.$target ) { return; } if ( wo.resizable_throttle ) { clearTimeout( vars.timer ); vars.timer = setTimeout( function() { ts.resizable.mouseMove( c, wo, event ); }, isNaN( wo.resizable_throttle ) ? 5 : wo.resizable_throttle ); } else { ts.resizable.mouseMove( c, wo, event ); } }) .bind( 'mouseup' + namespace, function() { if (!wo.resizable_vars.disabled) { return; } ts.resizable.toggleTextSelection( c, wo, false ); ts.resizable.stopResize( c, wo ); ts.resizable.setHandlePosition( c, wo ); }); // resizeEnd event triggered by scroller widget $( window ).bind( 'resize' + namespace + ' resizeEnd' + namespace, function() { ts.resizable.setHandlePosition( c, wo ); }); // right click to reset columns to default widths c.$table .bind( 'columnUpdate' + namespace, function() { ts.resizable.setHandlePosition( c, wo ); }) .find( 'thead:first' ) .add( $( c.namespace + '_extra_table' ).find( 'thead:first' ) ) .bind( 'contextmenu' + namespace, function() { // $.isEmptyObject() needs jQuery 1.4+; allow right click if already reset var allowClick = wo.resizable_vars.storedSizes.length === 0; ts.resizableReset( c.table ); ts.resizable.setHandlePosition( c, wo ); wo.resizable_vars.storedSizes = []; return allowClick; }); }, mouseMove : function( c, wo, event ) { if ( wo.resizable_vars.mouseXPosition === 0 || !wo.resizable_vars.$target ) { return; } // resize columns var column, total = 0, vars = wo.resizable_vars, $next = vars.$next, tar = vars.storedSizes[ vars.target ], leftEdge = event.pageX - vars.mouseXPosition; if ( vars.overflow ) { if ( tar + leftEdge > 0 ) { vars.storedSizes[ vars.target ] += leftEdge; ts.resizable.setWidth( vars.$target, vars.storedSizes[ vars.target ], true ); // update the entire table width for ( column = 0; column < c.columns; column++ ) { total += vars.storedSizes[ column ]; } ts.resizable.setWidth( c.$table.add( $( c.namespace + '_extra_table' ) ), total ); } if ( !$next.length ) { // if expanding right-most column, scroll the wrapper vars.$wrap[0].scrollLeft = c.$table.width(); } } else if ( vars.fullWidth ) { vars.storedSizes[ vars.target ] += leftEdge; vars.storedSizes[ vars.next ] -= leftEdge; ts.resizable.setWidths( c, wo ); } else { vars.storedSizes[ vars.target ] += leftEdge; ts.resizable.setWidths( c, wo ); } vars.mouseXPosition = event.pageX; // dynamically update sticky header widths c.$table.triggerHandler('stickyHeadersUpdate'); }, stopResize : function( c, wo ) { var vars = wo.resizable_vars; ts.resizable.updateStoredSizes( c, wo ); if ( vars.useStorage ) { // save all column widths ts.storage( c.table, ts.css.resizableStorage, vars.storedSizes ); ts.storage( c.table, 'tablesorter-table-resized-width', c.$table.width() ); } vars.mouseXPosition = 0; vars.$target = vars.$next = null; // will update stickyHeaders, just in case, see #912 c.$table.triggerHandler('stickyHeadersUpdate'); } }; // this widget saves the column widths if // $.tablesorter.storage function is included // ************************** ts.addWidget({ id: 'resizable', priority: 40, options: { resizable : true, // save column widths to storage resizable_addLastColumn : false, resizable_widths : [], resizable_throttle : false, // set to true (5ms) or any number 0-10 range resizable_targetLast : false, resizable_fullWidth : null }, init: function(table, thisWidget, c, wo) { ts.resizable.init( c, wo ); }, format: function( table, c, wo ) { ts.resizable.setHandlePosition( c, wo ); }, remove: function( table, c, wo, refreshing ) { if (wo.$resizable_container) { var namespace = c.namespace + 'tsresize'; c.$table.add( $( c.namespace + '_extra_table' ) ) .removeClass('hasResizable') .children( 'thead' ) .unbind( 'contextmenu' + namespace ); wo.$resizable_container.remove(); ts.resizable.toggleTextSelection( c, wo, false ); ts.resizableReset( table, refreshing ); $( document ).unbind( 'mousemove' + namespace + ' mouseup' + namespace ); } } }); ts.resizableReset = function( table, refreshing ) { $( table ).each(function(){ var index, $t, c = this.config, wo = c && c.widgetOptions, vars = wo.resizable_vars; if ( table && c && c.$headerIndexed.length ) { // restore the initial table width if ( vars.overflow && vars.tableWidth ) { ts.resizable.setWidth( c.$table, vars.tableWidth, true ); if ( vars.useStorage ) { ts.storage( table, 'tablesorter-table-resized-width', 'auto' ); } } for ( index = 0; index < c.columns; index++ ) { $t = c.$headerIndexed[ index ]; if ( wo.resizable_widths && wo.resizable_widths[ index ] ) { ts.resizable.setWidth( $t, wo.resizable_widths[ index ], vars.overflow ); } else if ( !$t.hasClass( 'resizable-false' ) ) { // don't clear the width of any column that is not resizable ts.resizable.setWidth( $t, '', vars.overflow ); } } // reset stickyHeader widths c.$table.triggerHandler( 'stickyHeadersUpdate' ); if ( ts.storage && !refreshing ) { ts.storage( this, ts.css.resizableStorage, {} ); } } }); }; })( jQuery, window );