' )
.addClass( classes.toolbar )
.insertBefore( $anchor );
}
this.$toolbar = $toolbar;
if( this.mode ) {
this.$toolbar.addClass( 'tablesaw-mode-' + this.mode );
}
};
Table.prototype.destroy = function() {
// Don’t remove the toolbar, just erase the classes on it.
// Some of the table features are not yet destroy-friendly.
this._getToolbar().each(function() {
this.className = this.className.replace( /\btablesaw-mode\-\w*\b/gi, '' );
});
var tableId = this.$table.attr( 'id' );
$( document ).off( "." + tableId );
$( window ).off( "." + tableId );
// other plugins
this.$table.trigger( events.destroy, [ this ] );
this.$table.removeData( pluginName );
};
// Collection method.
$.fn[ pluginName ] = function() {
return this.each( function() {
var $t = $( this );
if( $t.data( pluginName ) ){
return;
}
new Table( this );
});
};
var $doc = $( win.document );
$doc.on( "enhance.tablesaw", function( e ) {
// Cut the mustard
if( Tablesaw.mustard ) {
$( e.target ).find( initSelector ).filter( initFilterSelector )[ pluginName ]();
}
});
// Avoid a resize during scroll:
// Some Mobile devices trigger a resize during scroll (sometimes when
// doing elastic stretch at the end of the document or from the
// location bar hide)
var isScrolling = false;
var scrollTimeout;
$doc.on( "scroll.tablesaw", function() {
isScrolling = true;
win.clearTimeout( scrollTimeout );
scrollTimeout = win.setTimeout(function() {
isScrolling = false;
}, 300 ); // must be greater than the resize timeout below
});
var resizeTimeout;
$( win ).on( "resize", function() {
if( !isScrolling ) {
win.clearTimeout( resizeTimeout );
resizeTimeout = win.setTimeout(function() {
$doc.trigger( events.resize );
}, 150 ); // must be less than the scrolling timeout above.
}
});
}());
(function(){
var classes = {
stackTable: 'tablesaw-stack',
cellLabels: 'tablesaw-cell-label',
cellContentLabels: 'tablesaw-cell-content'
};
var data = {
key: 'tablesaw-stack'
};
var attrs = {
labelless: 'data-tablesaw-no-labels',
hideempty: 'data-tablesaw-hide-empty'
};
var Stack = function( element, tablesaw ) {
this.tablesaw = tablesaw;
this.$table = $( element );
this.labelless = this.$table.is( '[' + attrs.labelless + ']' );
this.hideempty = this.$table.is( '[' + attrs.hideempty + ']' );
this.$table.data( data.key, this );
};
Stack.prototype.init = function() {
this.$table.addClass( classes.stackTable );
if( this.labelless ) {
return;
}
var self = this;
this.$table.find( "th, td" ).filter(function() {
return !$( this ).closest( "thead" ).length;
}).filter(function() {
return !$( this ).closest( "tr" ).is( "[" + attrs.labelless + "]" ) &&
( !self.hideempty || !!$( this ).html() );
}).each(function() {
var $newHeader = $( document.createElement( "b" ) ).addClass( classes.cellLabels );
var $cell = $( this );
$( self.tablesaw._findPrimaryHeadersForCell( this ) ).each(function( index ) {
var $header = $( this.cloneNode( true ) );
// TODO decouple from sortable better
// Changed from .text() in https://github.com/filamentgroup/tablesaw/commit/b9c12a8f893ec192830ec3ba2d75f062642f935b
// to preserve structural html in headers, like
var $sortableButton = $header.find( ".tablesaw-sortable-btn" );
$header.find( ".tablesaw-sortable-arrow" ).remove();
// TODO decouple from checkall better
var $checkall = $header.find( "[data-tablesaw-checkall]" );
$checkall.closest( "label" ).remove();
if( $checkall.length ) {
$newHeader = $([]);
return;
}
if( index > 0 ) {
$newHeader.append( document.createTextNode( ", " ) );
}
$newHeader.append( $sortableButton.length ? $sortableButton[ 0 ].childNodes : $header[ 0 ].childNodes );
});
if( $newHeader.length && !$cell.find( "." + classes.cellContentLabels ).length ) {
$cell.wrapInner( "" );
}
// Update if already exists.
var $label = $cell.find( "." + classes.cellLabels );
if( !$label.length ) {
$cell.prepend( $newHeader );
} else { // only if changed
$label.replaceWith( $newHeader );
}
});
};
Stack.prototype.destroy = function() {
this.$table.removeClass( classes.stackTable );
this.$table.find( '.' + classes.cellLabels ).remove();
this.$table.find( '.' + classes.cellContentLabels ).each(function() {
$( this ).replaceWith( this.childNodes );
});
};
// on tablecreate, init
$( document ).on( Tablesaw.events.create, function( e, tablesaw ){
if( tablesaw.mode === 'stack' ){
var table = new Stack( tablesaw.table, tablesaw );
table.init();
}
}).on( Tablesaw.events.refresh, function( e, tablesaw ){
if( tablesaw.mode === 'stack' ){
$( tablesaw.table ).data( data.key ).init();
}
}).on( Tablesaw.events.destroy, function( e, tablesaw ){
if( tablesaw.mode === 'stack' ){
$( tablesaw.table ).data( data.key ).destroy();
}
});
}());
(function() {
var pluginName = "tablesawbtn",
methods = {
_create: function(){
return $( this ).each(function() {
$( this )
.trigger( "beforecreate." + pluginName )[ pluginName ]( "_init" )
.trigger( "create." + pluginName );
});
},
_init: function(){
var oEl = $( this ),
sel = this.getElementsByTagName( "select" )[ 0 ];
if( sel ) {
// TODO next major version: remove .btn-select
$( this )
.addClass( "btn-select tablesaw-btn-select" )[ pluginName ]( "_select", sel );
}
return oEl;
},
_select: function( sel ) {
var update = function( oEl, sel ) {
var opts = $( sel ).find( "option" );
var label = document.createElement( "span" );
var el;
var children;
var found = false;
label.setAttribute( "aria-hidden", "true" );
label.innerHTML = " ";
opts.each(function() {
var opt = this;
if( opt.selected ) {
label.innerHTML = opt.text;
}
});
children = oEl.childNodes;
if( opts.length > 0 ){
for( var i = 0, l = children.length; i < l; i++ ) {
el = children[ i ];
if( el && el.nodeName.toUpperCase() === "SPAN" ) {
oEl.replaceChild( label, el );
found = true;
}
}
if( !found ) {
oEl.insertBefore( label, oEl.firstChild );
}
}
};
update( this, sel );
$( this ).on( "change refresh", function() {
update( this, sel );
});
}
};
// Collection method.
$.fn[ pluginName ] = function( arrg, a, b, c ) {
return this.each(function() {
// if it's a method
if( arrg && typeof( arrg ) === "string" ){
return $.fn[ pluginName ].prototype[ arrg ].call( this, a, b, c );
}
// don't re-init
if( $( this ).data( pluginName + "active" ) ){
return $( this );
}
$( this ).data( pluginName + "active", true );
$.fn[ pluginName ].prototype._create.call( this );
});
};
// add methods
$.extend( $.fn[ pluginName ].prototype, methods );
}());
(function(){
var data = {
key: "tablesaw-coltoggle"
};
var ColumnToggle = function( element ) {
this.$table = $( element );
if( !this.$table.length ) {
return;
}
this.tablesaw = this.$table.data( "tablesaw" );
this.attributes = {
subrow: "data-tablesaw-subrow",
ignorerow: "data-tablesaw-ignorerow",
btnTarget: 'data-tablesaw-columntoggle-btn-target',
set: 'data-tablesaw-columntoggle-set'
};
this.classes = {
columnToggleTable: 'tablesaw-columntoggle',
columnBtnContain: 'tablesaw-columntoggle-btnwrap tablesaw-advance',
columnBtn: 'tablesaw-columntoggle-btn tablesaw-nav-btn down',
popup: 'tablesaw-columntoggle-popup',
priorityPrefix: 'tablesaw-priority-'
};
this.set = [];
this.$headers = this.tablesaw._getPrimaryHeaderCells();
this.$table.data( data.key, this );
};
// Column Toggle Sets (one column chooser can control multiple tables)
ColumnToggle.prototype.initSet = function() {
var set = this.$table.attr( this.attributes.set );
if( set ) {
// Should not include the current table
var table = this.$table[ 0 ];
this.set = $( "table[" + this.attributes.set + "='" + set + "']" ).filter(function() {
return this !== table;
}).get();
}
};
ColumnToggle.prototype.init = function() {
if( !this.$table.length ) {
return;
}
var tableId,
id,
$menuButton,
$popup,
$menu,
$btnContain,
self = this;
var cfg = this.tablesaw.getConfig({
getColumnToggleLabelTemplate: function( text ) {
return "";
}
});
this.$table.addClass( this.classes.columnToggleTable );
tableId = this.$table.attr( "id" );
id = tableId + "-popup";
$btnContain = $( "" );
// TODO next major version: remove .btn
$menuButton = $( "" +
"" + Tablesaw.i18n.columnBtnText + "" );
$popup = $( "" );
$menu = $( "
" );
this.$popup = $popup;
var hasNonPersistentHeaders = false;
this.$headers.each( function() {
var $this = $( this ),
priority = $this.attr("data-tablesaw-priority"),
$cells = self.$getCells( this );
if( priority && priority !== "persist" ) {
$cells.addClass( self.classes.priorityPrefix + priority );
$( cfg.getColumnToggleLabelTemplate( $this.text() ) )
.appendTo( $menu )
.find( "input[type=\"checkbox\"]" )
.data( "tablesaw-header", this );
hasNonPersistentHeaders = true;
}
});
if( !hasNonPersistentHeaders ) {
$menu.append( '
' );
}
$menu.appendTo( $popup );
function onToggleCheckboxChange( checkbox ) {
var checked = checkbox.checked;
var header = self.getHeaderFromCheckbox( checkbox );
var $cells = self.$getCells( header );
$cells[ !checked ? "addClass" : "removeClass" ]( "tablesaw-cell-hidden" );
$cells[ checked ? "addClass" : "removeClass" ]( "tablesaw-cell-visible" );
self.updateColspanIgnoredRows( checked, $( header ).add( header.cells ) );
self.$table.trigger( 'tablesawcolumns' );
}
// bind change event listeners to inputs - TODO: move to a private method?
$menu.find( 'input[type="checkbox"]' ).on( "change", function(e) {
onToggleCheckboxChange( e.target );
if( self.set.length ) {
var index;
$( self.$popup ).find( "input[type='checkbox']" ).each(function( j ) {
if( this === e.target ) {
index = j;
return false;
}
});
$( self.set ).each(function() {
var checkbox = $( this ).data( data.key ).$popup.find( "input[type='checkbox']" ).get( index );
if( checkbox ) {
checkbox.checked = e.target.checked;
onToggleCheckboxChange( checkbox );
}
});
}
});
$menuButton.appendTo( $btnContain );
// Use a different target than the toolbar
var $btnTarget = $( this.$table.attr( this.attributes.btnTarget ) );
$btnContain.appendTo( $btnTarget.length ? $btnTarget : this.tablesaw.$toolbar );
function closePopup( event ) {
// Click came from inside the popup, ignore.
if( event && $( event.target ).closest( "." + self.classes.popup ).length ) {
return;
}
$( document ).off( 'click.' + tableId );
$menuButton.removeClass( 'up' ).addClass( 'down' );
$btnContain.removeClass( 'visible' );
}
var closeTimeout;
function openPopup() {
$btnContain.addClass( 'visible' );
$menuButton.removeClass( 'down' ).addClass( 'up' );
$( document ).off( 'click.' + tableId, closePopup );
window.clearTimeout( closeTimeout );
closeTimeout = window.setTimeout(function() {
$( document ).on( 'click.' + tableId, closePopup );
}, 15 );
}
$menuButton.on( "click.tablesaw", function( event ) {
event.preventDefault();
if( !$btnContain.is( ".visible" ) ) {
openPopup();
} else {
closePopup();
}
});
$popup.appendTo( $btnContain );
this.$menu = $menu;
// Fix for iOS not rendering shadows correctly when using `-webkit-overflow-scrolling`
var $overflow = this.$table.closest( ".tablesaw-overflow" );
if( $overflow.css( "-webkit-overflow-scrolling" ) ) {
var timeout;
$overflow.on( "scroll", function() {
var $div = $( this );
window.clearTimeout( timeout );
timeout = window.setTimeout(function() {
$div.css( "-webkit-overflow-scrolling", "auto" );
window.setTimeout(function() {
$div.css( "-webkit-overflow-scrolling", "touch" );
}, 0 );
}, 100 );
});
}
$(window).on( Tablesaw.events.resize + "." + tableId, function(){
self.refreshToggle();
});
this.initSet();
this.refreshToggle();
};
ColumnToggle.prototype.updateColspanIgnoredRows = function( invisibleColumnCount, $cells ) {
this.$table.find( "[" + this.attributes.subrow + "],[" + this.attributes.ignorerow + "]" ).each(function() {
var $t = $( this );
var $td = $t.find( "td[colspan]" ).eq( 0 );
var excludedInvisibleColumns;
var colspan;
var originalColspan;
var modifier;
// increment or decrementing only (from a user triggered column show/hide)
if( invisibleColumnCount === true || invisibleColumnCount === false ) {
// unless the column being hidden is not included in the colspan
modifier = $cells.filter(function() {
return this === $td[ 0 ];
}).length ? ( invisibleColumnCount ? 1 : -1 ) : 0;
colspan = parseInt( $td.attr( "colspan" ), 10 ) + modifier;
} else {
// triggered from a resize or init
originalColspan = $td.data( "original-colspan" );
if( originalColspan ) {
colspan = originalColspan;
} else {
colspan = parseInt( $td.attr( "colspan" ), 10 );
$td.data( "original-colspan", colspan );
}
excludedInvisibleColumns = $t.find( "td" ).filter(function() {
return this !== $td[ 0 ] && $( this ).css( "display" ) === "none";
}).length;
colspan -= ( invisibleColumnCount - excludedInvisibleColumns );
}
// TODO add a colstart param so that this more appropriately selects colspan elements based on the column being hidden.
$td.attr( "colspan", colspan );
});
};
ColumnToggle.prototype.$getCells = function( th ) {
var self = this;
return $( th ).add( th.cells ).filter(function() {
var $t = $( this );
var $row = $t.parent();
var hasColspan = $t.is( "[colspan]" );
// no subrows or ignored rows (keep cells in ignored rows that do not have a colspan)
return !$row.is( "[" + self.attributes.subrow + "]" ) &&
( !$row.is( "[" + self.attributes.ignorerow + "]" ) || !hasColspan );
});
};
ColumnToggle.prototype.getHeaderFromCheckbox = function( checkbox ) {
return $( checkbox ).data( "tablesaw-header" );
};
ColumnToggle.prototype.refreshToggle = function() {
var self = this;
var invisibleColumns = 0;
this.$menu.find( "input" ).each( function() {
var header = self.getHeaderFromCheckbox( this );
var isVisible = self.$getCells( header ).eq( 0 ).css( "display" ) === "table-cell";
this.checked = isVisible;
if( !isVisible ) {
invisibleColumns++;
}
});
this.updateColspanIgnoredRows( invisibleColumns );
};
ColumnToggle.prototype.destroy = function() {
this.$table.removeClass( this.classes.columnToggleTable );
this.$table.find( 'th, td' ).each(function() {
var $cell = $( this );
$cell.removeClass( 'tablesaw-cell-hidden' )
.removeClass( 'tablesaw-cell-visible' );
this.className = this.className.replace( /\bui\-table\-priority\-\d\b/g, '' );
});
};
// on tablecreate, init
$( document ).on( Tablesaw.events.create, function( e, tablesaw ){
if( tablesaw.mode === 'columntoggle' ){
var table = new ColumnToggle( tablesaw.table );
table.init();
}
} );
$( document ).on( Tablesaw.events.destroy, function( e, tablesaw ){
if( tablesaw.mode === 'columntoggle' ){
$( tablesaw.table ).data( data.key ).destroy();
}
} );
}());
;(function() {
function getSortValue( cell ) {
var text = [];
$( cell.childNodes ).each(function() {
var $el = $( this );
if( $el.is( 'input, select' ) ) {
text.push( $el.val() );
} else if( $el.is( '.tablesaw-cell-label' ) ) {
} else {
text.push( ( $el.text() || '' ).replace(/^\s+|\s+$/g, '') );
}
});
return text.join( '' );
}
var pluginName = "tablesaw-sortable",
initSelector = "table[data-" + pluginName + "]",
sortableSwitchSelector = "[data-" + pluginName + "-switch]",
attrs = {
sortCol: "data-tablesaw-sortable-col",
defaultCol: "data-tablesaw-sortable-default-col",
numericCol: "data-tablesaw-sortable-numeric",
subRow: "data-tablesaw-subrow",
ignoreRow: "data-tablesaw-ignorerow"
},
classes = {
head: pluginName + "-head",
ascend: pluginName + "-ascending",
descend: pluginName + "-descending",
switcher: pluginName + "-switch",
tableToolbar: 'tablesaw-bar-section',
sortButton: pluginName + "-btn"
},
methods = {
_create: function( o ){
return $( this ).each(function() {
var init = $( this ).data( pluginName + "-init" );
if( init ) {
return false;
}
$( this )
.data( pluginName + "-init", true )
.trigger( "beforecreate." + pluginName )
[ pluginName ]( "_init" , o )
.trigger( "create." + pluginName );
});
},
_init: function(){
var el = $( this );
var tblsaw = el.data( "tablesaw" );
var heads;
var $switcher;
function addClassToHeads( h ){
$.each( h , function( i , v ){
$( v ).addClass( classes.head );
});
}
function makeHeadsActionable( h , fn ){
$.each( h , function( i , col ){
var b = $( "
" );
b.on( "click" , { col: col } , fn );
$( col ).wrapInner( b ).find( "button" ).append( "
" );
});
}
function clearOthers( headcells ){
$.each( headcells , function( i , v ){
var col = $( v );
col.removeAttr( attrs.defaultCol );
col.removeClass( classes.ascend );
col.removeClass( classes.descend );
});
}
function headsOnAction( e ){
if( $( e.target ).is( 'a[href]' ) ) {
return;
}
e.stopPropagation();
var headCell = $( e.target ).closest( "[" + attrs.sortCol + "]" ),
v = e.data.col,
newSortValue = heads.index( headCell[0] );
clearOthers( headCell.closest( "thead" ).find( "th" ).filter(function() {
return this !== headCell[ 0 ];
}) );
if( headCell.is( "." + classes.descend ) || !headCell.is( "." + classes.ascend ) ){
el[ pluginName ]( "sortBy" , v , true);
newSortValue += '_asc';
} else {
el[ pluginName ]( "sortBy" , v );
newSortValue += '_desc';
}
if( $switcher ) {
$switcher.find( 'select' ).val( newSortValue ).trigger( 'refresh' );
}
e.preventDefault();
}
function handleDefault( heads ){
$.each( heads , function( idx , el ){
var $el = $( el );
if( $el.is( "[" + attrs.defaultCol + "]" ) ){
if( !$el.is( "." + classes.descend ) ) {
$el.addClass( classes.ascend );
}
}
});
}
function addSwitcher( heads ){
$switcher = $( '' ).addClass( classes.switcher ).addClass( classes.tableToolbar );
var html = [ '
' );
$switcher.html( html.join('') );
var $firstChild = tblsaw.$toolbar.children().eq( 0 );
if( $firstChild.length ) {
$switcher.insertBefore( $firstChild );
} else {
$switcher.appendTo( tblsaw.$toolbar );
}
$switcher.find( '.tablesaw-btn' ).tablesawbtn();
$switcher.find( 'select' ).on( 'change', function() {
var val = $( this ).val().split( '_' ),
head = heads.eq( val[ 0 ] );
clearOthers( head.siblings() );
el[ pluginName ]( 'sortBy', head.get( 0 ), val[ 1 ] === 'asc' );
});
}
el.addClass( pluginName );
heads = el.children().filter( "thead" ).find( "th[" + attrs.sortCol + "]" );
addClassToHeads( heads );
makeHeadsActionable( heads , headsOnAction );
handleDefault( heads );
if( el.is( sortableSwitchSelector ) ) {
addSwitcher( heads );
}
},
sortRows: function( rows, colNum, ascending, col, tbody ){
function convertCells( cellArr, belongingToTbody ){
var cells = [];
$.each( cellArr, function( i , cell ){
var row = cell.parentNode;
var $row = $( row );
// next row is a subrow
var subrows = [];
var $next = $row.next();
while( $next.is( "[" + attrs.subRow + "]" ) ) {
subrows.push( $next[ 0 ] );
$next = $next.next();
}
var tbody = row.parentNode;
// current row is a subrow
if( $row.is( "[" + attrs.subRow + "]" ) ) {
} else if( tbody === belongingToTbody ) {
cells.push({
element: cell,
cell: getSortValue( cell ),
row: row,
subrows: subrows.length ? subrows : null,
ignored: $row.is( "[" + attrs.ignoreRow + "]" )
});
}
});
return cells;
}
function getSortFxn( ascending, forceNumeric ){
var fn,
regex = /[^\-\+\d\.]/g;
if( ascending ){
fn = function( a , b ){
if( a.ignored || b.ignored ) {
return 0;
}
if( forceNumeric ) {
return parseFloat( a.cell.replace( regex, '' ) ) - parseFloat( b.cell.replace( regex, '' ) );
} else {
return a.cell.toLowerCase() > b.cell.toLowerCase() ? 1 : -1;
}
};
} else {
fn = function( a , b ){
if( a.ignored || b.ignored ) {
return 0;
}
if( forceNumeric ) {
return parseFloat( b.cell.replace( regex, '' ) ) - parseFloat( a.cell.replace( regex, '' ) );
} else {
return a.cell.toLowerCase() < b.cell.toLowerCase() ? 1 : -1;
}
};
}
return fn;
}
function convertToRows( sorted ) {
var newRows = [], i, l;
for( i = 0, l = sorted.length ; i < l ; i++ ){
newRows.push( sorted[ i ].row );
if( sorted[ i ].subrows ) {
newRows.push( sorted[ i ].subrows );
}
}
return newRows;
}
var fn;
var sorted;
var cells = convertCells( col.cells, tbody );
var customFn = $( col ).data( 'tablesaw-sort' );
fn = ( customFn && typeof customFn === "function" ? customFn( ascending ) : false ) ||
getSortFxn( ascending, $( col ).is( '[' + attrs.numericCol + ']' ) && !$( col ).is( '[' + attrs.numericCol + '="false"]' ) );
sorted = cells.sort( fn );
rows = convertToRows( sorted );
return rows;
},
makeColDefault: function( col , a ){
var c = $( col );
c.attr( attrs.defaultCol , "true" );
if( a ){
c.removeClass( classes.descend );
c.addClass( classes.ascend );
} else {
c.removeClass( classes.ascend );
c.addClass( classes.descend );
}
},
sortBy: function( col , ascending ){
var el = $( this );
var colNum;
var tbl = el.data( "tablesaw" );
tbl.$tbody.each(function() {
var tbody = this;
var $tbody = $( this );
var rows = tbl.getBodyRows( tbody );
var sortedRows;
var map = tbl.headerMapping[ 0 ];
var j, k;
// find the column number that we’re sorting
for( j = 0, k = map.length; j < k; j++ ) {
if( map[ j ] === col ) {
colNum = j;
break;
}
}
sortedRows = el[ pluginName ]( "sortRows" , rows, colNum, ascending, col, tbody );
// replace Table rows
for( j = 0, k = sortedRows.length; j < k; j++ ) {
$tbody.append( sortedRows[ j ] );
}
});
el[ pluginName ]( "makeColDefault" , col , ascending );
el.trigger( "tablesaw-sorted" );
}
};
// Collection method.
$.fn[ pluginName ] = function( arrg ) {
var args = Array.prototype.slice.call( arguments , 1),
returnVal;
// if it's a method
if( arrg && typeof( arrg ) === "string" ){
returnVal = $.fn[ pluginName ].prototype[ arrg ].apply( this[0], args );
return (typeof returnVal !== "undefined")? returnVal:$(this);
}
// check init
if( !$( this ).data( pluginName + "-active" ) ){
$( this ).data( pluginName + "-active", true );
$.fn[ pluginName ].prototype._create.call( this , arrg );
}
return $(this);
};
// add methods
$.extend( $.fn[ pluginName ].prototype, methods );
$( document ).on( Tablesaw.events.create, function( e, Tablesaw ) {
if( Tablesaw.$table.is( initSelector ) ) {
Tablesaw.$table[ pluginName ]();
}
});
}());
(function(){
var classes = {
hideBtn: "disabled",
persistWidths: "tablesaw-fix-persist",
allColumnsVisible: 'tablesaw-all-cols-visible'
};
var attrs = {
disableTouchEvents: "data-tablesaw-no-touch"
};
function createSwipeTable( tbl, $table ){
var tblsaw = $table.data( "tablesaw" );
var $btns = $( "
" );
// TODO next major version: remove .btn
var $prevBtn = $( "
" ).appendTo( $btns );
// TODO next major version: remove .btn
var $nextBtn = $( "
" ).appendTo( $btns );
var $headerCells = tbl._getPrimaryHeaderCells();
var $headerCellsNoPersist = $headerCells.not( '[data-tablesaw-priority="persist"]' );
var headerWidths = [];
var $head = $( document.head || 'head' );
var tableId = $table.attr( 'id' );
if( !$headerCells.length ) {
throw new Error( "tablesaw swipe: no header cells found. Are you using
inside of ?" );
}
$table.addClass( "tablesaw-swipe" );
// Calculate initial widths
$headerCells.each(function() {
var width = this.offsetWidth;
headerWidths.push( width );
});
$btns.appendTo( tblsaw.$toolbar );
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( classes.persistWidths );
$( '#' + tableId + '-persist' ).remove();
}
function maintainWidths() {
var prefix = '#' + tableId + '.tablesaw-swipe ',
styles = [],
tableWidth = $table.width(),
hash = [],
newHash;
// save persistent column widths (as long as they take up less than 75% of table width)
$headerCells.each(function( index ) {
var width;
if( isPersistent( this ) ) {
width = this.offsetWidth;
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( '_' );
if( styles.length ) {
$table.addClass( classes.persistWidths );
var $style = $( '#' + tableId + '-persist' );
// If style element not yet added OR if the widths have changed
if( !$style.length || $style.data( 'tablesaw-hash' ) !== newHash ) {
// Remove existing
$style.remove();
$( '' )
.attr( 'id', tableId + '-persist' )
.data( 'tablesaw-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 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 ];
sums.push( sum );
// is persistent or is hidden
if( isPersist || sum > containerWidth ) {
visibleNonPersistantCount--;
}
});
// We need at least one column to swipe.
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 );
}
});
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;
}
}
maintainWidths();
hideColumn( $headerCellsNoPersist.get( pair[ 0 ] ) );
showColumn( $headerCellsNoPersist.get( pair[ 1 ] ) );
$table.trigger( 'tablesawcolumns' );
}
}
$prevBtn.add( $nextBtn ).on( "click", function( e ){
advance( !!$( e.target ).closest( $nextBtn ).length );
e.preventDefault();
});
function getCoord( event, key ) {
return ( event.touches || event.originalEvent.touches )[ 0 ][ key ];
}
if( !$table.is( "[" + attrs.disableTouchEvents + "]" ) ) {
$table
.on( "touchstart.swipetoggle", function( e ){
var originX = getCoord( e, 'pageX' );
var originY = getCoord( e, 'pageY' );
var x;
var y;
var scrollTop = window.pageYOffset;
$( win ).off( Tablesaw.events.resize, fakeBreakpoints );
$( this )
.on( "touchmove.swipetoggle", function( e ){
x = getCoord( e, 'pageX' );
y = getCoord( e, 'pageY' );
})
.on( "touchend.swipetoggle", function() {
var cfg = tbl.getConfig({
swipeHorizontalThreshold: 30,
swipeVerticalThreshold: 30
});
// This config code is a little awkward because shoestring doesn’t support deep $.extend
// Trying to work around when devs only override one of (not both) horizontalThreshold or
// verticalThreshold in their TablesawConfig.
// @TODO major version bump: remove cfg.swipe, move to just use the swipePrefix keys
var verticalThreshold = cfg.swipe ? cfg.swipe.verticalThreshold : cfg.swipeVerticalThreshold;
var horizontalThreshold = cfg.swipe ? cfg.swipe.horizontalThreshold : cfg.swipeHorizontalThreshold;
var isPageScrolled = Math.abs( window.pageYOffset - scrollTop ) >= verticalThreshold;
var isVerticalSwipe = Math.abs( y - originY ) >= verticalThreshold;
if( !isVerticalSwipe && !isPageScrolled ) {
if( x - originX < -1 * horizontalThreshold ){
advance( true );
}
if( x - originX > horizontalThreshold ){
advance( false );
}
}
window.setTimeout(function() {
$( win ).on( Tablesaw.events.resize, fakeBreakpoints );
}, 300);
$( this ).off( "touchmove.swipetoggle touchend.swipetoggle" );
});
});
}
$table
.on( "tablesawcolumns.swipetoggle", function(){
var canGoPrev = canAdvance( getPrev() );
var canGoNext = canAdvance( getNext() );
$prevBtn[ canGoPrev ? "removeClass" : "addClass" ]( classes.hideBtn );
$nextBtn[ canGoNext ? "removeClass" : "addClass" ]( classes.hideBtn );
tblsaw.$toolbar[ !canGoPrev && !canGoNext ? 'addClass' : 'removeClass' ]( classes.allColumnsVisible );
})
.on( "tablesawnext.swipetoggle", function(){
advance( true );
} )
.on( "tablesawprev.swipetoggle", function(){
advance( false );
} )
.on( Tablesaw.events.destroy + ".swipetoggle", function(){
var $t = $( this );
$t.removeClass( 'tablesaw-swipe' );
tblsaw.$toolbar.find( '.tablesaw-advance' ).remove();
$( win ).off( Tablesaw.events.resize, fakeBreakpoints );
$t.off( ".swipetoggle" );
})
.on( Tablesaw.events.refresh, function() {
// manual refresh
headerWidths = [];
$headerCells.each(function() {
var width = this.offsetWidth;
headerWidths.push( width );
});
fakeBreakpoints();
});
fakeBreakpoints();
$( win ).on( Tablesaw.events.resize, fakeBreakpoints );
}
// on tablecreate, init
$( document ).on( Tablesaw.events.create, function( e, tablesaw ){
if( tablesaw.mode === 'swipe' ){
createSwipeTable( tablesaw, tablesaw.$table );
}
} );
}());
;(function(){
var MiniMap = {
attr: {
init: 'data-tablesaw-minimap'
}
};
function createMiniMap( $table ){
var tblsaw = $table.data( "tablesaw" );
var $btns = $( '' );
var $dotNav = $( ' ' ).appendTo( $btns );
var hideDot = 'tablesaw-advance-dots-hide';
var $headerCells = $table.find( 'thead th' );
// populate dots
$headerCells.each(function(){
$dotNav.append( ' ' );
});
$btns.appendTo( tblsaw.$toolbar );
function showMinimap( $table ) {
var mq = $table.attr( MiniMap.attr.init );
return !mq || win.matchMedia && win.matchMedia( mq ).matches;
}
function showHideNav(){
if( !showMinimap( $table ) ) {
$btns.css( "display", "none" );
return;
}
$btns.css( "display", "block" );
// show/hide dots
var dots = $dotNav.find( "li" ).removeClass( hideDot );
$table.find( "thead th" ).each(function(i){
if( $( this ).css( "display" ) === "none" ){
dots.eq( i ).addClass( hideDot );
}
});
}
// run on init and resize
showHideNav();
$( win ).on( Tablesaw.events.resize, showHideNav );
$table
.on( "tablesawcolumns.minimap", function(){
showHideNav();
})
.on( Tablesaw.events.destroy + ".minimap", function(){
var $t = $( this );
tblsaw.$toolbar.find( '.tablesaw-advance' ).remove();
$( win ).off( Tablesaw.events.resize, showHideNav );
$t.off( ".minimap" );
});
}
// on tablecreate, init
$( document ).on( Tablesaw.events.create, function( e, tablesaw ){
if( ( tablesaw.mode === 'swipe' || tablesaw.mode === 'columntoggle' ) && tablesaw.$table.is( '[ ' + MiniMap.attr.init + ']' ) ){
createMiniMap( tablesaw.$table );
}
} );
}());
;(function() {
var S = {
selectors: {
init: 'table[data-tablesaw-mode-switch]'
},
attributes: {
excludeMode: 'data-tablesaw-mode-exclude'
},
classes: {
main: 'tablesaw-modeswitch',
toolbar: 'tablesaw-bar-section'
},
modes: [ 'stack', 'swipe', 'columntoggle' ],
init: function( table ) {
var $table = $( table );
var tblsaw = $table.data( "tablesaw" );
var ignoreMode = $table.attr( S.attributes.excludeMode );
var $toolbar = tblsaw.$toolbar;
var $switcher = $( '' ).addClass( S.classes.main + ' ' + S.classes.toolbar );
var html = [ '' );
$switcher.html( html.join( '' ) );
var $otherToolbarItems = $toolbar.find( '.tablesaw-advance' ).eq( 0 );
if( $otherToolbarItems.length ) {
$switcher.insertBefore( $otherToolbarItems );
} else {
$switcher.appendTo( $toolbar );
}
$switcher.find( '.tablesaw-btn' ).tablesawbtn();
$switcher.find( 'select' ).on( 'change', function( event ) {
return S.onModeChange.call( table, event, $( this ).val() );
});
},
onModeChange: function( event, val ) {
var $table = $( this );
var tblsaw = $table.data( "tablesaw" );
var $switcher = tblsaw.$toolbar.find( '.' + S.classes.main );
$switcher.remove();
tblsaw.destroy();
$table.attr( 'data-tablesaw-mode', val );
$table.tablesaw();
}
};
$( win.document ).on( Tablesaw.events.create, function( e, Tablesaw ) {
if( Tablesaw.$table.is( S.selectors.init ) ) {
S.init( Tablesaw.table );
}
});
})();
(function(){
var pluginName = "tablesawCheckAll";
function CheckAll( tablesaw ) {
this.tablesaw = tablesaw;
this.$table = tablesaw.$table;
this.attr = "data-tablesaw-checkall";
this.checkAllSelector = "[" + this.attr + "]";
this.forceCheckedSelector = "[" + this.attr + "-checked]";
this.forceUncheckedSelector = "[" + this.attr + "-unchecked]";
this.checkboxSelector = "input[type=\"checkbox\"]";
this.$triggers = null;
this.$checkboxes = null;
if( this.$table.data( pluginName ) ) {
return;
}
this.$table.data( pluginName, this );
this.init();
}
CheckAll.prototype._filterCells = function( $checkboxes ) {
return $checkboxes.filter(function() {
return !$( this ).closest( "tr" ).is( "[data-tablesaw-subrow],[data-tablesaw-ignorerow]" );
}).find( this.checkboxSelector ).not( this.checkAllSelector );
};
// With buttons you can use a scoping selector like: data-tablesaw-checkall="#my-scoped-id input[type='checkbox']"
CheckAll.prototype.getCheckboxesForButton = function( button ) {
return this._filterCells( $( $( button ).attr( this.attr ) ) );
};
CheckAll.prototype.getCheckboxesForCheckbox = function( checkbox ) {
return this._filterCells( $( $( checkbox ).closest( "th" )[ 0 ].cells ) );
};
CheckAll.prototype.init = function() {
var self = this;
this.$table.find( this.checkAllSelector ).each(function() {
var $trigger = $( this );
if( $trigger.is( self.checkboxSelector ) ) {
self.addCheckboxEvents( this );
} else {
self.addButtonEvents( this );
}
});
};
CheckAll.prototype.addButtonEvents = function( trigger ) {
var self = this;
// Update body checkboxes when header checkbox is changed
$( trigger ).on( "click", function( event ) {
event.preventDefault();
var $checkboxes = self.getCheckboxesForButton( this );
var allChecked = true;
$checkboxes.each(function() {
if( !this.checked ) {
allChecked = false;
}
});
var setChecked;
if( $( this ).is( self.forceCheckedSelector ) ) {
setChecked = true;
} else if( $( this ).is( self.forceUncheckedSelector ) ) {
setChecked = false;
} else {
setChecked = allChecked ? false : true;
}
$checkboxes.each(function() {
this.checked = setChecked;
$( this ).trigger( "change." + pluginName );
});
});
};
CheckAll.prototype.addCheckboxEvents = function( trigger ) {
var self = this;
// Update body checkboxes when header checkbox is changed
$( trigger ).on( "change", function() {
var setChecked = this.checked;
self.getCheckboxesForCheckbox( this ).each(function() {
this.checked = setChecked;
});
});
var $checkboxes = self.getCheckboxesForCheckbox( trigger );
// Update header checkbox when body checkboxes are changed
$checkboxes.on( "change." + pluginName, function() {
var checkedCount = 0;
$checkboxes.each(function() {
if( this.checked ) {
checkedCount++;
}
});
var allSelected = checkedCount === $checkboxes.length;
trigger.checked = allSelected;
// only indeterminate if some are selected (not all and not none)
trigger.indeterminate = checkedCount !== 0 && !allSelected;
});
};
// on tablecreate, init
$( document ).on( Tablesaw.events.create, function( e, tablesaw ){
new CheckAll( tablesaw );
});
}());
}));
|