/*! Tablesaw - v2.0.2 - 2015-10-28 * https://github.com/filamentgroup/tablesaw * Copyright (c) 2015 Filament Group; Licensed */ /* * tablesaw: A set of plugins for responsive tables * Stack and Column Toggle tables * Copyright (c) 2013 Filament Group, Inc. * MIT License */ if( typeof Tablesaw === "undefined" ) { Tablesaw = { i18n: { modes: [ 'Stack', 'Swipe', 'Toggle' ], columns: 'Columns', columnBtnText: 'Columns', columnsDialogError: 'No eligible columns.', sort: 'Sort' }, // cut the mustard mustard: 'querySelector' in document && ( !window.blackberry || window.WebKitPoint ) && !window.operamini }; } if( !Tablesaw.config ) { Tablesaw.config = {}; } if( Tablesaw.mustard ) { jQuery( document.documentElement ).addClass( 'tablesaw-enhanced' ); } ;(function( $ ) { var pluginName = "table", classes = { toolbar: "tablesaw-bar" }, events = { create: "tablesawcreate", destroy: "tablesawdestroy", refresh: "tablesawrefresh" }, defaultMode = "stack", initSelector = "table[data-tablesaw-mode],table[data-tablesaw-sortable]"; var Table = function( element ) { if( !element ) { throw new Error( "Tablesaw requires an element." ); } this.table = element; this.$table = $( element ); this.mode = this.$table.attr( "data-tablesaw-mode" ) || defaultMode; this.init(); }; Table.prototype.init = function() { // assign an id if there is none if ( !this.$table.attr( "id" ) ) { this.$table.attr( "id", pluginName + "-" + Math.round( Math.random() * 10000 ) ); } this.createToolbar(); var colstart = this._initCells(); this.$table.trigger( events.create, [ this, colstart ] ); }; Table.prototype._initCells = function() { var colstart, thrs = this.table.querySelectorAll( "thead tr" ), self = this; $( thrs ).each( function(){ var coltally = 0; $( this ).children().each( function(){ var span = parseInt( this.getAttribute( "colspan" ), 10 ), sel = ":nth-child(" + ( coltally + 1 ) + ")"; colstart = coltally + 1; if( span ){ for( var k = 0; k < span - 1; k++ ){ coltally++; sel += ", :nth-child(" + ( coltally + 1 ) + ")"; } } // Store "cells" data on header as a reference to all cells in the same column as this TH this.cells = self.$table.find("tr").not( thrs[0] ).not( this ).children().filter( sel ); coltally++; }); }); return colstart; }; Table.prototype.refresh = function() { this._initCells(); this.$table.trigger( events.refresh ); }; Table.prototype.createToolbar = function() { // Insert the toolbar // TODO move this into a separate component var $toolbar = this.$table.prev().filter( '.' + classes.toolbar ); if( !$toolbar.length ) { $toolbar = $( '
' ) .addClass( classes.toolbar ) .insertBefore( this.$table ); } this.$toolbar = $toolbar; if( this.mode ) { this.$toolbar.addClass( 'mode-' + this.mode ); } }; Table.prototype.destroy = function() { // Don’t remove the toolbar. Some of the table features are not yet destroy-friendly. this.$table.prev().filter( '.' + classes.toolbar ).each(function() { this.className = this.className.replace( /\bmode\-\w*\b/gi, '' ); }); var tableId = this.$table.attr( 'id' ); $( document ).unbind( "." + tableId ); $( window ).unbind( "." + tableId ); // other plugins this.$table.trigger( events.destroy, [ this ] ); this.$table.removeAttr( 'data-tablesaw-mode' ); this.$table.removeData( pluginName ); }; // Collection method. $.fn[ pluginName ] = function() { return this.each( function() { var $t = $( this ); if( $t.data( pluginName ) ){ return; } var table = new Table( this ); $t.data( pluginName, table ); }); }; $( document ).on( "enhance.tablesaw", function( e ) { // Cut the mustard if( Tablesaw.mustard ) { $( e.target ).find( initSelector )[ pluginName ](); } }); }( jQuery )); ;(function( win, $, undefined ){ var classes = { stackTable: 'tablesaw-stack', cellLabels: 'tablesaw-cell-label', cellContentLabels: 'tablesaw-cell-content' }; var data = { obj: 'tablesaw-stack' }; var attrs = { labelless: 'data-tablesaw-no-labels', hideempty: 'data-tablesaw-hide-empty' }; var Stack = function( element ) { this.$table = $( element ); this.labelless = this.$table.is( '[' + attrs.labelless + ']' ); this.hideempty = this.$table.is( '[' + attrs.hideempty + ']' ); if( !this.labelless ) { // allHeaders references headers, plus all THs in the thead, which may include several rows, or not this.allHeaders = this.$table.find( "th" ); } this.$table.data( data.obj, this ); }; Stack.prototype.init = function( colstart ) { this.$table.addClass( classes.stackTable ); if( this.labelless ) { return; } // get headers in reverse order so that top-level headers are appended last var reverseHeaders = $( this.allHeaders ); var hideempty = this.hideempty; // create the hide/show toggles reverseHeaders.each(function(){ var $t = $( this ), $cells = $( this.cells ).filter(function() { return !$( this ).parent().is( "[" + attrs.labelless + "]" ) && ( !hideempty || !$( this ).is( ":empty" ) ); }), hierarchyClass = $cells.not( this ).filter( "thead th" ).length && " tablesaw-cell-label-top", // TODO reduce coupling with sortable $sortableButton = $t.find( ".tablesaw-sortable-btn" ), html = $sortableButton.length ? $sortableButton.html() : $t.html(); if( html !== "" ){ if( hierarchyClass ){ var iteration = parseInt( $( this ).attr( "colspan" ), 10 ), filter = ""; if( iteration ){ filter = "td:nth-child("+ iteration +"n + " + ( colstart ) +")"; } $cells.filter( filter ).prepend( "" + html + "" ); } else { $cells.wrapInner( "" ); $cells.prepend( "" + html + "" ); } } }); }; 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( "tablesawcreate", function( e, Tablesaw, colstart ){ if( Tablesaw.mode === 'stack' ){ var table = new Stack( Tablesaw.table ); table.init( colstart ); } } ); $( document ).on( "tablesawdestroy", function( e, Tablesaw ){ if( Tablesaw.mode === 'stack' ){ $( Tablesaw.table ).data( data.obj ).destroy(); } } ); }( this, jQuery )); ;(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 ) { $( this ) .addClass( "btn-select" ) [ pluginName ]( "_select", sel ); } return oEl; }, _select: function( sel ) { var update = function( oEl, sel ) { var opts = $( sel ).find( "option" ), label, el, children; opts.each(function() { var opt = this; if( opt.selected ) { label = document.createTextNode( 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.nodeType === 3 ) { oEl.replaceChild( label, el ); } } } }; update( this, sel ); $( this ).bind( "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 ); } // otherwise, init $( this ).data( pluginName + "active", true ); $.fn[ pluginName ].prototype._create.call( this ); }); }; // add methods $.extend( $.fn[ pluginName ].prototype, methods ); }( jQuery )); ;(function( win, $, undefined ){ var ColumnToggle = function( element ) { this.$table = $( element ); 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-', // TODO duplicate class, also in tables.js toolbar: 'tablesaw-bar' }; // Expose headers and allHeaders properties on the widget // headers references the THs within the first TR in the table this.headers = this.$table.find( 'tr:first > th' ); this.$table.data( 'tablesaw-coltoggle', this ); }; ColumnToggle.prototype.init = function() { var tableId, id, $menuButton, $popup, $menu, $btnContain, self = this; this.$table.addClass( this.classes.columnToggleTable ); tableId = this.$table.attr( "id" ); id = tableId + "-popup"; $btnContain = $( "
" ); $menuButton = $( "" + "" + Tablesaw.i18n.columnBtnText + "" ); $popup = $( "
" ); $menu = $( "
" ); var hasNonPersistentHeaders = false; $( this.headers ).not( "td" ).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 ); $("" ) .appendTo( $menu ) .children( 0 ) .data( "tablesaw-header", this ); hasNonPersistentHeaders = true; } }); if( !hasNonPersistentHeaders ) { $menu.append( '' ); } $menu.appendTo( $popup ); // bind change event listeners to inputs - TODO: move to a private method? $menu.find( 'input[type="checkbox"]' ).on( "change", function(e) { var checked = e.target.checked; self.$getCellsFromCheckbox( e.target ) .toggleClass( "tablesaw-cell-hidden", !checked ) .toggleClass( "tablesaw-cell-visible", checked ); self.$table.trigger( 'tablesawcolumns' ); }); $menuButton.appendTo( $btnContain ); $btnContain.appendTo( this.$table.prev().filter( '.' + this.classes.toolbar ) ); var closeTimeout; function openPopup() { $btnContain.addClass( 'visible' ); $menuButton.removeClass( 'down' ).addClass( 'up' ); $( document ).unbind( 'click.' + tableId, closePopup ); window.clearTimeout( closeTimeout ); closeTimeout = window.setTimeout(function() { $( document ).one( 'click.' + tableId, closePopup ); }, 15 ); } function closePopup( event ) { // Click came from inside the popup, ignore. if( event && $( event.target ).closest( "." + self.classes.popup ).length ) { return; } $( document ).unbind( 'click.' + tableId ); $menuButton.removeClass( 'up' ).addClass( 'down' ); $btnContain.removeClass( 'visible' ); } $menuButton.on( "click.tablesaw", function( event ) { event.preventDefault(); if( !$btnContain.is( ".visible" ) ) { openPopup(); } else { closePopup(); } }); $popup.appendTo( $btnContain ); this.$menu = $menu; $(window).on( "resize." + tableId, function(){ self.refreshToggle(); }); this.refreshToggle(); }; ColumnToggle.prototype.$getCells = function( th ) { return $( th ).add( th.cells ); }; ColumnToggle.prototype.$getCellsFromCheckbox = function( checkbox ) { var th = $( checkbox ).data( "tablesaw-header" ); return this.$getCells( th ); }; ColumnToggle.prototype.refreshToggle = function() { var self = this; this.$menu.find( "input" ).each( function() { this.checked = self.$getCellsFromCheckbox( this ).eq( 0 ).css( "display" ) === "table-cell"; }); }; ColumnToggle.prototype.refreshPriority = function(){ var self = this; $(this.headers).not( "td" ).each( function() { var $this = $( this ), priority = $this.attr("data-tablesaw-priority"), $cells = $this.add( this.cells ); if( priority && priority !== "persist" ) { $cells.addClass( self.classes.priorityPrefix + priority ); } }); }; ColumnToggle.prototype.destroy = function() { // table toolbars, document and window .tableId events // removed in parent tables.js destroy method 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( "tablesawcreate", function( e, Tablesaw ){ if( Tablesaw.mode === 'columntoggle' ){ var table = new ColumnToggle( Tablesaw.table ); table.init(); } } ); $( document ).on( "tablesawdestroy", function( e, Tablesaw ){ if( Tablesaw.mode === 'columntoggle' ){ $( Tablesaw.table ).data( 'tablesaw-coltoggle' ).destroy(); } } ); }( this, jQuery )); ;(function( win, $, undefined ){ $.extend( Tablesaw.config, { swipe: { horizontalThreshold: 15, verticalThreshold: 30 } }); function isIE8() { var div = document.createElement('div'), all = div.getElementsByTagName('i'); div.innerHTML = ''; return !!all.length; } function createSwipeTable( $table ){ var $btns = $( "
" ), $prevBtn = $( "" ).appendTo( $btns ), $nextBtn = $( "" ).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 inside of ?" ); } // 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 ) { $( '' ) .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 )); ;(function( $ ) { function getSortValue( cell ) { return $.map( cell.childNodes, function( el ) { var $el = $( el ); if( $el.is( 'input, select' ) ) { return $el.val(); } else if( $el.hasClass( 'tablesaw-cell-label' ) ) { return; } return $.trim( $el.text() ); }).join( '' ); } var pluginName = "tablesaw-sortable", initSelector = "table[data-" + pluginName + "]", sortableSwitchSelector = "[data-" + pluginName + "-switch]", attrs = { defaultCol: "data-tablesaw-sortable-default-col", numericCol: "data-tablesaw-sortable-numeric" }, classes = { head: pluginName + "-head", ascend: pluginName + "-ascending", descend: pluginName + "-descending", switcher: pluginName + "-switch", tableToolbar: 'tablesaw-toolbar', sortButton: pluginName + "-btn" }, methods = { _create: function( o ){ return $( this ).each(function() { var init = $( this ).data( "init" + pluginName ); if( init ) { return false; } $( this ) .data( "init"+ pluginName, true ) .trigger( "beforecreate." + pluginName ) [ pluginName ]( "_init" , o ) .trigger( "create." + pluginName ); }); }, _init: function(){ var el = $( this ), heads, $switcher; var addClassToTable = function(){ el.addClass( pluginName ); }, addClassToHeads = function( h ){ $.each( h , function( i , v ){ $( v ).addClass( classes.head ); }); }, makeHeadsActionable = function( h , fn ){ $.each( h , function( i , v ){ var b = $( "