From b533400ff2876f8b4fa5a283e53b9887e7c59bd8 Mon Sep 17 00:00:00 2001 From: Michael Ryvkin Date: Mon, 25 Apr 2016 14:45:55 -0400 Subject: [PATCH] Added support for search, sorting, numbers, localization --- .../dataTables.alphabetSearch.css | 8 +- .../dataTables.alphabetSearch.js | 244 ++++++++++++++++-- .../dataTables.alphabetSearch.min.js | 12 +- 3 files changed, 232 insertions(+), 32 deletions(-) diff --git a/features/alphabetSearch/dataTables.alphabetSearch.css b/features/alphabetSearch/dataTables.alphabetSearch.css index 3258e1e..5837e33 100644 --- a/features/alphabetSearch/dataTables.alphabetSearch.css +++ b/features/alphabetSearch/dataTables.alphabetSearch.css @@ -10,7 +10,7 @@ div.alphabet span { color: #3174c7; cursor: pointer; text-align: center; - width: 3.5% + width: 3.5%; } div.alphabet span:hover { @@ -25,7 +25,7 @@ div.alphabet span.empty { color: red; } -div.alphabetInfo { +div.alphabet_info { display: block; position: absolute; background-color: #111; @@ -37,3 +37,7 @@ div.alphabetInfo { text-align: center; z-index: 1; } + +tr.alphabet_group, tr.alphabet_group:hover { + background-color: rgba(0,0,0,0.15) !important; +} diff --git a/features/alphabetSearch/dataTables.alphabetSearch.js b/features/alphabetSearch/dataTables.alphabetSearch.js index a43eabc..ed948a6 100644 --- a/features/alphabetSearch/dataTables.alphabetSearch.js +++ b/features/alphabetSearch/dataTables.alphabetSearch.js @@ -1,4 +1,4 @@ -/*! AlphabetSearch for DataTables v1.0.0 +/*! AlphabetSearch for DataTables v1.1.0 * 2014 SpryMedia Ltd - datatables.net/license */ @@ -6,16 +6,17 @@ * @summary AlphabetSearch * @description Show an set of alphabet buttons alongside a table providing * search input options - * @version 1.0.0 + * @version 1.1.1 * @file dataTables.alphabetSearch.js * @author SpryMedia Ltd (www.sprymedia.co.uk) * @contact www.sprymedia.co.uk/contact * @copyright Copyright 2014 SpryMedia Ltd. - * + * * License MIT - http://datatables.net/license/mit * * For more detailed information please see: * http://datatables.net/blog/2014-09-22 + * http://www.gyrocode.com/articles/jquery-datatables-alphabetical-search */ (function(){ @@ -23,7 +24,7 @@ // Search function $.fn.dataTable.Api.register( 'alphabetSearch()', function ( searchTerm ) { this.iterator( 'table', function ( context ) { - context.alphabetSearch = searchTerm; + context.alphabetSearch.letter = searchTerm; } ); return this; @@ -34,7 +35,8 @@ $.fn.dataTable.Api.register( 'alphabetSearch.recalc()', function ( searchTerm ) this.iterator( 'table', function ( context ) { draw( new $.fn.dataTable.Api( context ), - $('div.alphabet', this.table().container()) + $('div.alphabet', this.table().container()), + context ); } ); @@ -45,18 +47,81 @@ $.fn.dataTable.Api.register( 'alphabetSearch.recalc()', function ( searchTerm ) // Search plug-in $.fn.dataTable.ext.search.push( function ( context, searchData ) { // Ensure that there is a search applied to this table before running it - if ( ! context.alphabetSearch ) { + if ( ! context.alphabetSearch.letterSearch ) { return true; } - if ( searchData[0].charAt(0) === context.alphabetSearch ) { - return true; + var letter = searchData[context.alphabetSearch.column] + .toString() + .replace(/<.*?>/g, '') + .charAt(0).toUpperCase(); + + + if(context.alphabetSearch.letterSearch !== '#'){ + if ( letter === context.alphabetSearch.letterSearch ) { + return true; + } + } else { + if(/\d/.test(letter)){ + return true; + } } return false; } ); +// Order plug-in +// +// NOTES: If sorting by alphabetized column +// there would be two calls to this method +$.fn.dataTable.ext.order['alphabetSearch'] = function ( context, col ) +{ + var order_col = this.api().order()[0][0]; + var order_method = this.api().order()[0][1]; + + // If sorting by column other than the one being alphabetized + if(order_col !== context.alphabetSearch.column){ + context.alphabetSearch.pass = 0; + } + + var data = this.api().column( col, { order: 'index' } ).data().map( function (value, index) { + // If sorting by alphabetized column + return (order_col === context.alphabetSearch.column) + ? ( + // If first pass + ( !context.alphabetSearch.pass ) + // Ignore + ? + '' + // Otherwise, if it's a second pass + : + ( + // If method is ascending sort + ( order_method === 'asc' ) + // Return first letter + ? value.charAt(0) + : String.fromCharCode(65535 - value.charCodeAt(0)) + ) + ) + // Otherwise, if sorting by column other than the one being alphabetized, + // return first letter + : value.charAt(0); + + } ); + + // If sorting by alphabetized column + if(order_col === context.alphabetSearch.column){ + // If pass is not defined, set it to 0 + if(!context.alphabetSearchPass){ context.alphabetSearch.pass = 0; } + // Increment pass counter and reset it to 0 if it's a second pass + context.alphabetSearch.pass = (context.alphabetSearch.pass + 1) % 2; + } + + return data; +}; + + // Private support methods function bin ( data ) { var letter, bins = {}; @@ -67,6 +132,8 @@ function bin ( data ) { .replace(/<.*?>/g, '') .charAt(0).toUpperCase(); + if(/\d/.test(letter)){ letter = '#'; } + if ( bins[letter] ) { bins[letter]++; } @@ -78,33 +145,67 @@ function bin ( data ) { return bins; } -function draw ( table, alphabet ) +function draw ( table, alphabet, context ) { alphabet.empty(); - alphabet.append( 'Search: ' ); + alphabet.append( context.oLanguage.alphabetSearch.infoDisplay + ': ' ); - var columnData = table.column(0).data(); + var columnData = table.column(context.alphabetSearch.column, { search: 'applied' } ).data(); var bins = bin( columnData ); - $('') + $('') .data( 'letter', '' ) .data( 'match-count', columnData.length ) - .html( 'None' ) + .html( context.oLanguage.alphabetSearch.infoAll ) .appendTo( alphabet ); - for ( var i=0 ; i<26 ; i++ ) { - var letter = String.fromCharCode( 65 + i ); + for ( var i=0 ; i') .data( 'letter', letter ) .data( 'match-count', bins[letter] || 0 ) - .addClass( ! bins[letter] ? 'empty' : '' ) - .html( letter ) + .addClass( + (! bins[letter] ? 'empty' : '') + + ((context.alphabetSearch.letter === letter) ? ' active' : '') + ) + .html( + (letter === '#') ? '0-9' : letter + ) .appendTo( alphabet ); } - $('
') + $('
') .appendTo( alphabet ); + + + // Perform second rendering + // needed to filter search results by letter + // NOTE: Future optimization is needed to avoid rendering twice + // when no search is performed + + // If letter is selected + if(context.alphabetSearch.letter){ + // Apply filtering by letter + context.alphabetSearch.letterSearch = context.alphabetSearch.letter; + + // Redraw table + table.draw(); + + // Remove filtering by letter + context.alphabetSearch.letterSearch = ''; + } + + // Handle search event here only once + // when alphabet panel has been drawn + // because we are performing two-step rendering + // that could trigger search hanlder when not needed + table.one('search', function (e, context) { + var api = new $.fn.dataTable.Api( context ); + + // Redraw alphabet panel + api.alphabetSearch.recalc(); + }); } @@ -112,7 +213,76 @@ $.fn.dataTable.AlphabetSearch = function ( context ) { var table = new $.fn.dataTable.Api( context ); var alphabet = $('
'); - draw( table, alphabet ); + // Language + context.oLanguage.alphabetSearch = + $.extend( + { + 'alphabet': '#ABCDEFGHIJKLMNOPQRSTUVXYZ', + 'infoDisplay': 'Display', + 'infoAll': 'All' + }, + ((context.oLanguage.alphabetSearch) + ? context.oLanguage.alphabetSearch + : {} + ) + ); + // Convert alphabet to uppercase + context.oLanguage.alphabetSearch.alphabet.toUpperCase(); + + context.alphabetSearch = + $.extend( + { + column: 0 + }, + $.isPlainObject(context.oInit.alphabetSearch) ? context.oInit.alphabetSearch : {}, + { + letter: '', + letterSearch: '', + pass: 0 + } + ); + + // Set required "orderDataType" ("sSortDataType") for a column + if(context.alphabetSearch.column >= 0 && context.alphabetSearch.column < context.aoColumns.length){ + context.aoColumns[context.alphabetSearch.column].sSortDataType = 'alphabetSearch'; + } + + // Add column containing names to a list of columns + // where ordering will be always applied to the table + if( context.hasOwnProperty('aaSortingFixed') + && typeof context.aaSortingFixed === 'object' ) + { + if( $.isArray(context.aaSortingFixed) ){ + if( context.aaSortingFixed.length && !$.isArray( context.aaSortingFixed[0] ) ) { + // 1D array + context.aaSortingFixed = [[context.alphabetSearch.column, 'asc'], context.aaSortingFixed]; + + } else { + // 2D array + context.aaSortingFixed.unshift([context.alphabetSearch.column, 'asc']); + } + } else { + if( !context.aaSortingFixed.hasOwnProperty('pre') ){ + context.aaSortingFixed.pre = []; + } + + if( context.aaSortingFixed.pre.length && !$.isArray( context.aaSortingFixed.pre[0] ) ) { + // 1D array + context.aaSortingFixed.pre = [[context.alphabetSearch.column, 'asc'], context.aaSortingFixed.pre]; + + } else { + // 2D array + context.aaSortingFixed.pre.unshift([context.alphabetSearch.column, 'asc']); + } + } + + } else { + context.aaSortingFixed = [context.alphabetSearch.column, 'asc']; + } + + + draw( table, alphabet, context ); + // Trigger a search alphabet.on( 'click', 'span', function () { @@ -128,7 +298,7 @@ $.fn.dataTable.AlphabetSearch = function ( context ) { alphabet .on( 'mouseenter', 'span', function () { alphabet - .find('div.alphabetInfo') + .find('div.alphabet_info') .css( { opacity: 1, left: $(this).position().left, @@ -138,10 +308,41 @@ $.fn.dataTable.AlphabetSearch = function ( context ) { } ) .on( 'mouseleave', 'span', function () { alphabet - .find('div.alphabetInfo') + .find('div.alphabet_info') .css('opacity', 0); } ); + table.on('draw', function (e, context) { + var api = new $.fn.dataTable.Api( context ); + + // Total number of column nodes + var col_total = api.columns().nodes().length; + + var rows = api.rows({ page: 'current' }).nodes(); + var group_last = null; + + api.column(context.alphabetSearch.column, { page: 'current' }).data().each(function (name, index){ + var group = name.charAt(0).toUpperCase(); + + if (group_last !== group) { + $(rows).eq(index).before( + '' + group + '' + ); + + group_last = group; + } + }); + + // If there are no rows found and letter is selected + if(!rows.length && context.alphabetSearch){ + var letter = (context.alphabetSearch.letter === '#') ? '0-9' : context.alphabetSearch.letter; + + $(api.table().body()).prepend( + '' + letter + '' + ); + } + }); + // API method to get the alphabet container node this.node = function () { return alphabet; @@ -162,4 +363,3 @@ $.fn.dataTable.ext.feature.push( { }()); - diff --git a/features/alphabetSearch/dataTables.alphabetSearch.min.js b/features/alphabetSearch/dataTables.alphabetSearch.min.js index 033b500..efd2482 100644 --- a/features/alphabetSearch/dataTables.alphabetSearch.min.js +++ b/features/alphabetSearch/dataTables.alphabetSearch.min.js @@ -1,8 +1,4 @@ -/*! - AlphabetSearch for DataTables v1.0.0 - 2014 SpryMedia Ltd - datatables.net/license -*/ -(function(){function f(b,c){c.empty();c.append("Search: ");for(var a=b.column(0).data(),d,e={},g=0,f=a.length;g/g,"").charAt(0).toUpperCase(),e[d]?e[d]++:e[d]=1;$('').data("letter","").data("match-count",a.length).html("None").appendTo(c);for(a=0;26>a;a++)d=String.fromCharCode(65+a),$("").data("letter",d).data("match-count",e[d]||0).addClass(!e[d]?"empty":"").html(d).appendTo(c);$('
').appendTo(c)} -$.fn.dataTable.Api.register("alphabetSearch()",function(b){this.iterator("table",function(c){c.alphabetSearch=b});return this});$.fn.dataTable.Api.register("alphabetSearch.recalc()",function(){this.iterator("table",function(b){f(new $.fn.dataTable.Api(b),$("div.alphabet",this.table().container()))});return this});$.fn.dataTable.ext.search.push(function(b,c){return!b.alphabetSearch||c[0].charAt(0)===b.alphabetSearch?!0:!1});$.fn.dataTable.AlphabetSearch=function(b){var c=new $.fn.dataTable.Api(b), -a=$('
');f(c,a);a.on("click","span",function(){a.find(".active").removeClass("active");$(this).addClass("active");c.alphabetSearch($(this).data("letter")).draw()});a.on("mouseenter","span",function(){a.find("div.alphabetInfo").css({opacity:1,left:$(this).position().left,width:$(this).width()}).html($(this).data("match-count"))}).on("mouseleave","span",function(){a.find("div.alphabetInfo").css("opacity",0)});this.node=function(){return a}};$.fn.DataTable.AlphabetSearch=$.fn.dataTable.AlphabetSearch; -$.fn.dataTable.ext.feature.push({fnInit:function(b){return(new $.fn.dataTable.AlphabetSearch(b)).node()},cFeature:"A"})})(); +/*! AlphabetSearch for DataTables v1.1.0 + * 2014 SpryMedia Ltd - datatables.net/license + */ +(function(){$.fn.dataTable.Api.register("alphabetSearch()",function(searchTerm){this.iterator("table",function(context){context.alphabetSearch.letter=searchTerm;});return this;});$.fn.dataTable.Api.register("alphabetSearch.recalc()",function(searchTerm){this.iterator("table",function(context){draw(new $.fn.dataTable.Api(context),$("div.alphabet",this.table().container()),context);});return this;});$.fn.dataTable.ext.search.push(function(context,searchData){if(!context.alphabetSearch.letterSearch){return true;}var letter=searchData[context.alphabetSearch.column].toString().replace(/<.*?>/g,"").charAt(0).toUpperCase();if(context.alphabetSearch.letterSearch!=="#"){if(letter===context.alphabetSearch.letterSearch){return true;}}else{if(/\d/.test(letter)){return true;}}return false;});$.fn.dataTable.ext.order["alphabetSearch"]=function(context,col){var order_col=this.api().order()[0][0];var order_method=this.api().order()[0][1];if(order_col!==context.alphabetSearch.column){context.alphabetSearch.pass=0;}var data=this.api().column(col,{order:"index"}).data().map(function(value,index){return(order_col===context.alphabetSearch.column)?((!context.alphabetSearch.pass)?"":((order_method==="asc")?value.charAt(0):String.fromCharCode(65535-value.charCodeAt(0)))):value.charAt(0);});if(order_col===context.alphabetSearch.column){if(!context.alphabetSearchPass){context.alphabetSearch.pass=0;}context.alphabetSearch.pass=(context.alphabetSearch.pass+1)%2;}return data;};function bin(data){var letter,bins={};for(var i=0,ien=data.length;i/g,"").charAt(0).toUpperCase();if(/\d/.test(letter)){letter="#";}if(bins[letter]){bins[letter]++;}else{bins[letter]=1;}}return bins;}function draw(table,alphabet,context){alphabet.empty();alphabet.append(context.oLanguage.alphabetSearch.infoDisplay+": ");var columnData=table.column(context.alphabetSearch.column,{search:"applied"}).data();var bins=bin(columnData);$('').data("letter","").data("match-count",columnData.length).html(context.oLanguage.alphabetSearch.infoAll).appendTo(alphabet);for(var i=0;i").data("letter",letter).data("match-count",bins[letter]||0).addClass((!bins[letter]?"empty":"")+((context.alphabetSearch.letter===letter)?" active":"")).html((letter==="#")?"0-9":letter).appendTo(alphabet);}$('
').appendTo(alphabet);if(context.alphabetSearch.letter){context.alphabetSearch.letterSearch=context.alphabetSearch.letter;table.draw();context.alphabetSearch.letterSearch="";}table.one("search",function(e,context){var api=new $.fn.dataTable.Api(context);api.alphabetSearch.recalc();});}$.fn.dataTable.AlphabetSearch=function(context){var table=new $.fn.dataTable.Api(context);var alphabet=$('
');context.oLanguage.alphabetSearch=$.extend({"alphabet":"#ABCDEFGHIJKLMNOPQRSTUVXYZ","infoDisplay":"Display","infoAll":"All"},((context.oLanguage.alphabetSearch)?context.oLanguage.alphabetSearch:{}));context.oLanguage.alphabetSearch.alphabet.toUpperCase();context.alphabetSearch=$.extend({column:0},$.isPlainObject(context.oInit.alphabetSearch)?context.oInit.alphabetSearch:{},{letter:"",letterSearch:"",pass:0});if(context.alphabetSearch.column>=0&&context.alphabetSearch.column'+group+"");group_last=group;}});if(!rows.length&&context.alphabetSearch){var letter=(context.alphabetSearch.letter==="#")?"0-9":context.alphabetSearch.letter;$(api.table().body()).prepend(''+letter+"");}});this.node=function(){return alphabet;};};$.fn.DataTable.AlphabetSearch=$.fn.dataTable.AlphabetSearch;$.fn.dataTable.ext.feature.push({fnInit:function(settings){var search=new $.fn.dataTable.AlphabetSearch(settings);return search.node();},cFeature:"A"});}()); \ No newline at end of file