From 2b7594f52d7163ed86bfff09c6b42a3249355f3a Mon Sep 17 00:00:00 2001 From: Timothy J Warren Date: Fri, 10 Nov 2017 09:03:20 -0500 Subject: [PATCH] Add javascript table sorting --- app/Resources/views/base.html.twig | 2 + web/css/app.css | 18 +++ web/js/app.js | 10 +- web/js/table-sort.js | 247 +++++++++++++++++++++++++++++ 4 files changed, 276 insertions(+), 1 deletion(-) create mode 100644 web/js/table-sort.js diff --git a/app/Resources/views/base.html.twig b/app/Resources/views/base.html.twig index b6b4fef..8f35141 100644 --- a/app/Resources/views/base.html.twig +++ b/app/Resources/views/base.html.twig @@ -42,6 +42,8 @@ + + {% block javascripts %}{% endblock %} diff --git a/web/css/app.css b/web/css/app.css index cb721bd..3f0119f 100644 --- a/web/css/app.css +++ b/web/css/app.css @@ -1,3 +1,21 @@ +/* ----------------------------------------------------------------------------- + Table sorting +------------------------------------------------------------------------------*/ +table th, +.ascend, +.descend { + vertical-align:text-bottom; +} +table thead th::before { + content: " ↕\00a0"; +} +table thead .ascend::before { + content: " ↑\00a0"; +} +table thead .descend::before { + content: " ↓\00a0"; +} + .page-pad { margin: 1rem 2rem; } diff --git a/web/js/app.js b/web/js/app.js index 5b80fd1..5bd79bd 100644 --- a/web/js/app.js +++ b/web/js/app.js @@ -1 +1,9 @@ -$(document).foundation() +$(document).foundation(); + +(function() { + var tables = document.getElementsByTagName('table'); + + if (tables.length > 0) { + tsorter.create(tables[0], 1); + } +}()); diff --git a/web/js/table-sort.js b/web/js/table-sort.js new file mode 100644 index 0000000..3cc3656 --- /dev/null +++ b/web/js/table-sort.js @@ -0,0 +1,247 @@ +/*! + * tsorter 2.0.0 - Copyright 2015 Terrill Dent, http://terrill.ca + * JavaScript HTML Table Sorter + * Released under MIT license, http://terrill.ca/sorting/tsorter/LICENSE + */ +var tsorter = (function() +{ + 'use strict'; + + var sorterPrototype, + addEvent, + removeEvent, + hasEventListener = !!document.addEventListener; + + if( !Object.create ){ + // Define Missing Function + Object.create = function( prototype ) { + var Obj = function(){return undefined;}; + Obj.prototype = prototype; + return new Obj(); + }; + } + + sorterPrototype = { + + getCell: function(row) + { + var that = this; + return that.trs[row].cells[that.column]; + }, + + /* SORT + * Sorts a particular column. If it has been sorted then call reverse + * if not, then use quicksort to get it sorted. + * Sets the arrow direction in the headers. + * @param oTH - the table header cell () object that is clicked + */ + sort: function( e ) + { + var that = this, + th = e.target; + + // TODO: make sure target 'th' is not a child element of a + // We can't use currentTarget because of backwards browser support + // IE6,7,8 don't have it. + + // set the data retrieval function for this column + that.column = th.cellIndex; + that.get = that.getAccessor( th.getAttribute('data-tsorter') ); + + if( that.prevCol === that.column ) + { + // if already sorted, reverse + th.className = th.className !== 'descend' ? 'descend' : 'ascend'; + that.reverseTable(); + } + else + { + // not sorted - call quicksort + th.className = 'descend'; + if( that.prevCol !== -1 && that.ths[that.prevCol].className !== 'exc_cell'){ + that.ths[that.prevCol].className = ''; + } + that.quicksort(0, that.trs.length); + } + that.prevCol = that.column; + }, + + /* + * Choose Data Accessor Function + * @param: the html structure type (from the data-type attribute) + */ + getAccessor: function(sortType) + { + var that = this, + accessors = that.accessors; + + if( accessors && accessors[ sortType ] ){ + return accessors[ sortType ]; + } + + switch( sortType ) + { + case "link": + return function(row){ + return that.getCell(row).firstChild.firstChild.nodeValue; + }; + case "input": + return function(row){ + return that.getCell(row).firstChild.value; + }; + case "numeric": + return function(row){ + return parseFloat( that.getCell(row).firstChild.nodeValue.replace(/\D/g,''), 10 ); + }; + case "number-range": + return function(row){ + return parseFloat( + String(this.getCell(row).childNodes[0].nodeValue) + .split('–')[0] + .trim() + .replace('ƒ', '') + ); + }; + case "in-a-link": + return function(row){ + return String( + this.getCell(row).getElementsByTagName('a')[0].childNodes[0].nodeValue + ).toLowerCase(); + }; + default: /* Plain Text */ + return function(row){ + return that.getCell(row).firstChild.nodeValue.toLowerCase(); + }; + } + }, + + /* + * Exchange + * A complicated way of exchanging two rows in a table. + * Exchanges rows at index i and j + */ + exchange: function(i, j) + { + var that = this, + tbody = that.tbody, + trs = that.trs, + tmpNode; + + if( i === j+1 ) { + tbody.insertBefore(trs[i], trs[j]); + } else if( j === i+1 ) { + tbody.insertBefore(trs[j], trs[i]); + } else { + tmpNode = tbody.replaceChild(trs[i], trs[j]); + if( !trs[i] ) { + tbody.appendChild(tmpNode); + } else { + tbody.insertBefore(tmpNode, trs[i]); + } + } + }, + + /* + * REVERSE TABLE + * Reverses a table ordering + */ + reverseTable: function() + { + var that = this, + i; + + for( i = 1; i < that.trs.length; i++ ) { + that.tbody.insertBefore( that.trs[i], that.trs[0] ); + } + }, + + /* + * QUICKSORT + * @param: lo - the low index of the array to sort + * @param: hi - the high index of the array to sort + */ + quicksort: function(lo, hi) + { + var i, j, pivot, + that = this; + + if( hi <= lo+1 ){ return; } + + if( (hi - lo) === 2 ) { + if(that.get(hi-1) > that.get(lo)) { + that.exchange(hi-1, lo); + } + return; + } + + i = lo + 1; + j = hi - 1; + + if( that.get(lo) > that.get( i) ){ that.exchange( i, lo); } + if( that.get( j) > that.get(lo) ){ that.exchange(lo, j); } + if( that.get(lo) > that.get( i) ){ that.exchange( i, lo); } + + pivot = that.get(lo); + + while(true) { + j--; + while(pivot > that.get(j)){ j--; } + i++; + while(that.get(i) > pivot){ i++; } + if(j <= i){ break; } + that.exchange(i, j); + } + that.exchange(lo, j); + + if((j-lo) < (hi-j)) { + that.quicksort(lo, j); + that.quicksort(j+1, hi); + } else { + that.quicksort(j+1, hi); + that.quicksort(lo, j); + } + }, + + init: function( table, initialSortedColumn, customDataAccessors ){ + var that = this, + i; + + if( typeof table === 'string' ){ + table = document.getElementById(table); + } + + that.table = table; + that.ths = table.getElementsByTagName("th"); + that.tbody = table.tBodies[0]; + that.trs = that.tbody.getElementsByTagName("tr"); + that.prevCol = ( initialSortedColumn && initialSortedColumn > 0 ) ? initialSortedColumn : -1; + that.accessors = customDataAccessors; + that.boundSort = that.sort.bind( that ); + + for( i = 0; i < that.ths.length; i++ ) { + that.ths[i].addEventListener('click', that.boundSort, false); + } + }, + + destroy: function(){ + var that = this, + i; + + if( that.ths ){ + for( i = 0; i < that.ths.length; i++ ) { + that.ths[i].removeEventListener('click', that.boundSort, false); + } + } + } + }; + + // Create a new sorter given a table element + return { + create: function( table, initialSortedColumn, customDataAccessors ) + { + var sorter = Object.create( sorterPrototype ); + sorter.init( table, initialSortedColumn, customDataAccessors ); + return sorter; + } + }; +}());