/*
|
* tablesaw: A set of plugins for responsive tables
|
* Swipe Toggle: swipe gesture (or buttons) to navigate which columns are shown.
|
* Copyright (c) 2013 Filament Group, Inc.
|
* MIT License
|
*/
|
|
;(function( win, $, undefined ){
|
|
$.extend( Tablesaw.config, {
|
swipe: {
|
horizontalThreshold: 15,
|
verticalThreshold: 30
|
}
|
});
|
|
function isIE8() {
|
var div = document.createElement('div'),
|
all = div.getElementsByTagName('i');
|
|
div.innerHTML = '<!--[if lte IE 8]><i></i><![endif]-->';
|
|
return !!all.length;
|
}
|
|
function createSwipeTable( $table ){
|
|
var $btns = $( "<div class='tablesaw-advance'></div>" ),
|
$prevBtn = $( "<a href='#' class='tablesaw-nav-btn btn btn-micro left' title='Previous Column'></a>" ).appendTo( $btns ),
|
$nextBtn = $( "<a href='#' class='tablesaw-nav-btn btn btn-micro right' title='Next Column'></a>" ).appendTo( $btns ),
|
hideBtn = 'disabled',
|
persistWidths = 'tablesaw-fix-persist',
|
$headerCells = $table.find( "thead th" ),
|
$headerCellsNoPersist = $headerCells.not( '[data-tablesaw-priority="persist"]' ),
|
headerWidths = [],
|
$head = $( document.head || 'head' ),
|
tableId = $table.attr( 'id' ),
|
// TODO switch this to an nth-child feature test
|
supportsNthChild = !isIE8();
|
|
if( !$headerCells.length ) {
|
throw new Error( "tablesaw swipe: no header cells found. Are you using <th> inside of <thead>?" );
|
}
|
|
// Calculate initial widths
|
$table.css('width', 'auto');
|
$headerCells.each(function() {
|
headerWidths.push( $( this ).outerWidth() );
|
});
|
$table.css( 'width', '' );
|
|
$btns.appendTo( $table.prev().filter( '.tablesaw-bar' ) );
|
|
$table.addClass( "tablesaw-swipe" );
|
|
if( !tableId ) {
|
tableId = 'tableswipe-' + Math.round( Math.random() * 10000 );
|
$table.attr( 'id', tableId );
|
}
|
|
function $getCells( headerCell ) {
|
return $( headerCell.cells ).add( headerCell );
|
}
|
|
function showColumn( headerCell ) {
|
$getCells( headerCell ).removeClass( 'tablesaw-cell-hidden' );
|
}
|
|
function hideColumn( headerCell ) {
|
$getCells( headerCell ).addClass( 'tablesaw-cell-hidden' );
|
}
|
|
function persistColumn( headerCell ) {
|
$getCells( headerCell ).addClass( 'tablesaw-cell-persist' );
|
}
|
|
function isPersistent( headerCell ) {
|
return $( headerCell ).is( '[data-tablesaw-priority="persist"]' );
|
}
|
|
function unmaintainWidths() {
|
$table.removeClass( persistWidths );
|
$( '#' + tableId + '-persist' ).remove();
|
}
|
|
function maintainWidths() {
|
var prefix = '#' + tableId + '.tablesaw-swipe ',
|
styles = [],
|
tableWidth = $table.width(),
|
hash = [],
|
newHash;
|
|
$headerCells.each(function( index ) {
|
var width;
|
if( isPersistent( this ) ) {
|
width = $( this ).outerWidth();
|
|
// Only save width on non-greedy columns (take up less than 75% of table width)
|
if( width < tableWidth * 0.75 ) {
|
hash.push( index + '-' + width );
|
styles.push( prefix + ' .tablesaw-cell-persist:nth-child(' + ( index + 1 ) + ') { width: ' + width + 'px; }' );
|
}
|
}
|
});
|
newHash = hash.join( '_' );
|
|
$table.addClass( persistWidths );
|
|
var $style = $( '#' + tableId + '-persist' );
|
// If style element not yet added OR if the widths have changed
|
if( !$style.length || $style.data( 'hash' ) !== newHash ) {
|
// Remove existing
|
$style.remove();
|
|
if( styles.length ) {
|
$( '<style>' + styles.join( "\n" ) + '</style>' )
|
.attr( 'id', tableId + '-persist' )
|
.data( 'hash', newHash )
|
.appendTo( $head );
|
}
|
}
|
}
|
|
function getNext(){
|
var next = [],
|
checkFound;
|
|
$headerCellsNoPersist.each(function( i ) {
|
var $t = $( this ),
|
isHidden = $t.css( "display" ) === "none" || $t.is( ".tablesaw-cell-hidden" );
|
|
if( !isHidden && !checkFound ) {
|
checkFound = true;
|
next[ 0 ] = i;
|
} else if( isHidden && checkFound ) {
|
next[ 1 ] = i;
|
|
return false;
|
}
|
});
|
|
return next;
|
}
|
|
function getPrev(){
|
var next = getNext();
|
return [ next[ 1 ] - 1 , next[ 0 ] - 1 ];
|
}
|
|
function nextpair( fwd ){
|
return fwd ? getNext() : getPrev();
|
}
|
|
function canAdvance( pair ){
|
return pair[ 1 ] > -1 && pair[ 1 ] < $headerCellsNoPersist.length;
|
}
|
|
function matchesMedia() {
|
var matchMedia = $table.attr( "data-tablesaw-swipe-media" );
|
return !matchMedia || ( "matchMedia" in win ) && win.matchMedia( matchMedia ).matches;
|
}
|
|
function fakeBreakpoints() {
|
if( !matchesMedia() ) {
|
return;
|
}
|
|
var extraPaddingPixels = 20,
|
containerWidth = $table.parent().width(),
|
persist = [],
|
sum = 0,
|
sums = [],
|
visibleNonPersistantCount = $headerCells.length;
|
|
$headerCells.each(function( index ) {
|
var $t = $( this ),
|
isPersist = $t.is( '[data-tablesaw-priority="persist"]' );
|
|
persist.push( isPersist );
|
|
sum += headerWidths[ index ] + ( isPersist ? 0 : extraPaddingPixels );
|
sums.push( sum );
|
|
// is persistent or is hidden
|
if( isPersist || sum > containerWidth ) {
|
visibleNonPersistantCount--;
|
}
|
});
|
|
var needsNonPersistentColumn = visibleNonPersistantCount === 0;
|
|
$headerCells.each(function( index ) {
|
if( persist[ index ] ) {
|
|
// for visual box-shadow
|
persistColumn( this );
|
return;
|
}
|
|
if( sums[ index ] <= containerWidth || needsNonPersistentColumn ) {
|
needsNonPersistentColumn = false;
|
showColumn( this );
|
} else {
|
hideColumn( this );
|
}
|
});
|
|
if( supportsNthChild ) {
|
unmaintainWidths();
|
}
|
$table.trigger( 'tablesawcolumns' );
|
}
|
|
function advance( fwd ){
|
var pair = nextpair( fwd );
|
if( canAdvance( pair ) ){
|
if( isNaN( pair[ 0 ] ) ){
|
if( fwd ){
|
pair[0] = 0;
|
}
|
else {
|
pair[0] = $headerCellsNoPersist.length - 1;
|
}
|
}
|
|
if( supportsNthChild ) {
|
maintainWidths();
|
}
|
|
hideColumn( $headerCellsNoPersist.get( pair[ 0 ] ) );
|
showColumn( $headerCellsNoPersist.get( pair[ 1 ] ) );
|
|
$table.trigger( 'tablesawcolumns' );
|
}
|
}
|
|
$prevBtn.add( $nextBtn ).click(function( e ){
|
advance( !!$( e.target ).closest( $nextBtn ).length );
|
e.preventDefault();
|
});
|
|
function getCoord( event, key ) {
|
return ( event.touches || event.originalEvent.touches )[ 0 ][ key ];
|
}
|
|
$table
|
.bind( "touchstart.swipetoggle", function( e ){
|
var originX = getCoord( e, 'pageX' ),
|
originY = getCoord( e, 'pageY' ),
|
x,
|
y;
|
|
$( win ).off( "resize", fakeBreakpoints );
|
|
$( this )
|
.bind( "touchmove", function( e ){
|
x = getCoord( e, 'pageX' );
|
y = getCoord( e, 'pageY' );
|
var cfg = Tablesaw.config.swipe;
|
if( Math.abs( x - originX ) > cfg.horizontalThreshold && Math.abs( y - originY ) < cfg.verticalThreshold ) {
|
e.preventDefault();
|
}
|
})
|
.bind( "touchend.swipetoggle", function(){
|
var cfg = Tablesaw.config.swipe;
|
if( Math.abs( y - originY ) < cfg.verticalThreshold ) {
|
if( x - originX < -1 * cfg.horizontalThreshold ){
|
advance( true );
|
}
|
if( x - originX > cfg.horizontalThreshold ){
|
advance( false );
|
}
|
}
|
|
window.setTimeout(function() {
|
$( win ).on( "resize", fakeBreakpoints );
|
}, 300);
|
$( this ).unbind( "touchmove touchend" );
|
});
|
|
})
|
.bind( "tablesawcolumns.swipetoggle", function(){
|
$prevBtn[ canAdvance( getPrev() ) ? "removeClass" : "addClass" ]( hideBtn );
|
$nextBtn[ canAdvance( getNext() ) ? "removeClass" : "addClass" ]( hideBtn );
|
})
|
.bind( "tablesawnext.swipetoggle", function(){
|
advance( true );
|
} )
|
.bind( "tablesawprev.swipetoggle", function(){
|
advance( false );
|
} )
|
.bind( "tablesawdestroy.swipetoggle", function(){
|
var $t = $( this );
|
|
$t.removeClass( 'tablesaw-swipe' );
|
$t.prev().filter( '.tablesaw-bar' ).find( '.tablesaw-advance' ).remove();
|
$( win ).off( "resize", fakeBreakpoints );
|
|
$t.unbind( ".swipetoggle" );
|
});
|
|
fakeBreakpoints();
|
$( win ).on( "resize", fakeBreakpoints );
|
}
|
|
|
|
// on tablecreate, init
|
$( document ).on( "tablesawcreate", function( e, Tablesaw ){
|
|
if( Tablesaw.mode === 'swipe' ){
|
createSwipeTable( Tablesaw.$table );
|
}
|
|
} );
|
|
}( this, jQuery ));
|