mirror of
https://github.com/Mottie/tablesorter.git
synced 2024-11-15 23:54:22 +00:00
394 lines
14 KiB
JavaScript
394 lines
14 KiB
JavaScript
/*! Widget: resizable - updated 11/4/2015 (v2.24.3) */
|
|
/*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 = '<style>' +
|
|
'body.' + ts.css.resizableNoSelect + ' { -ms-user-select: none; -moz-user-select: -moz-none;' +
|
|
'-khtml-user-select: none; -webkit-user-select: none; user-select: none; }' +
|
|
'.' + ts.css.resizableContainer + ' { position: relative; height: 1px; }' +
|
|
// make handle z-index > than stickyHeader z-index, so the handle stays above sticky header
|
|
'.' + ts.css.resizableHandle + ' { position: absolute; display: inline-block; width: 8px;' +
|
|
'top: 1px; cursor: ew-resize; z-index: 3; user-select: none; -moz-user-select: none; }' +
|
|
'</style>';
|
|
$(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('<span>').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('<span>');
|
|
}
|
|
*/
|
|
|
|
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 = $( '<div class="' + ts.css.resizableContainer + '">' )
|
|
.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 ) {
|
|
$( '<div class="' + ts.css.resizableHandle + '">' )
|
|
.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 );
|