From 56e7758af28eecafccac99878415f6403c89b41e Mon Sep 17 00:00:00 2001 From: Tomas Cohen Arazi Date: Mon, 27 Apr 2020 02:28:59 -0300 Subject: [PATCH] Bug 25287: Add columns_settings capabilities to API datatables wrapper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds the code that is used for handling columns settings on datatables and allows passing the columns_settings information to the API-centric datatable. To test, you need bug 25288, which uses this features. Signed-off-by: Tomas Cohen Arazi Signed-off-by: Frédéric Demians Works with bug 24561. Make working bug 25288 --- koha-tmpl/intranet-tmpl/prog/js/datatables.js | 131 +++- .../opac-tmpl/bootstrap/js/datatables.js | 588 ++++++++++++++++-- 2 files changed, 678 insertions(+), 41 deletions(-) diff --git a/koha-tmpl/intranet-tmpl/prog/js/datatables.js b/koha-tmpl/intranet-tmpl/prog/js/datatables.js index f1594d4b07..5c8ab9e419 100644 --- a/koha-tmpl/intranet-tmpl/prog/js/datatables.js +++ b/koha-tmpl/intranet-tmpl/prog/js/datatables.js @@ -509,7 +509,7 @@ jQuery.fn.dataTable.ext.errMode = function(settings, note, message) { (function($) { - $.fn.api = function(options) { + $.fn.api = function(options, columns_settings, add_filters) { var settings = null; if(options) { if(!options.criteria || ['contains', 'starts_with', 'ends_with', 'exact'].indexOf(options.criteria.toLowerCase()) === -1) options.criteria = 'contains'; @@ -520,6 +520,10 @@ jQuery.fn.dataTable.ext.errMode = function(settings, note, message) { 'serverSide': true, 'searching': true, 'pagingType': 'full_numbers', + 'processing': true, + 'language': { + 'emptyTable': (options.emptyTable) ? options.emptyTable : _("No data available in table") + }, 'ajax': { 'type': 'GET', 'cache': true, @@ -592,6 +596,131 @@ jQuery.fn.dataTable.ext.errMode = function(settings, note, message) { } }, options); } + + var counter = 0; + var hidden_ids = []; + var included_ids = []; + + $(columns_settings).each( function() { + var named_id = $( 'thead th[data-colname="' + this.columnname + '"]', this ).index( 'th' ); + var used_id = settings.bKohaColumnsUseNames ? named_id : counter; + if ( used_id == -1 ) return; + + if ( this['is_hidden'] == "1" ) { + hidden_ids.push( used_id ); + } + if ( this['cannot_be_toggled'] == "0" ) { + included_ids.push( used_id ); + } + counter++; + }); + + var exportColumns = ":visible:not(.noExport)"; + if( settings.hasOwnProperty("exportColumns") ){ + // A custom buttons configuration has been passed from the page + exportColumns = settings["exportColumns"]; + } + + var export_format = { + body: function ( data, row, column, node ) { + var newnode = $(node); + + if ( newnode.find(".noExport").length > 0 ) { + newnode = newnode.clone(); + newnode.find(".noExport").remove(); + } + + return newnode.text().replace( /\n/g, ' ' ).trim(); + } + } + + var export_buttons = [ + { + extend: 'excelHtml5', + text: _("Excel"), + exportOptions: { + columns: exportColumns, + format: export_format + }, + }, + { + extend: 'csvHtml5', + text: _("CSV"), + exportOptions: { + columns: exportColumns, + format: export_format + }, + }, + { + extend: 'copyHtml5', + text: _("Copy"), + exportOptions: { + columns: exportColumns, + format: export_format + }, + }, + { + extend: 'print', + text: _("Print"), + exportOptions: { + columns: exportColumns, + format: export_format + }, + } + ]; + + settings[ "buttons" ] = [ + { + fade: 100, + className: "dt_button_clear_filter", + titleAttr: _("Clear filter"), + enabled: false, + text: ' ' + _("Clear filter") + '', + action: function ( e, dt, node, config ) { + dt.search( "" ).draw("page"); + node.addClass("disabled"); + } + } + ]; + + if( included_ids.length > 0 ){ + settings[ "buttons" ].push( + { + extend: 'colvis', + fade: 100, + columns: included_ids, + className: "columns_controls", + titleAttr: _("Columns settings"), + text: ' ' + _("Columns") + '', + exportOptions: { + columns: exportColumns + } + } + ); + } + + settings[ "buttons" ].push( + { + extend: 'collection', + autoClose: true, + fade: 100, + className: "export_controls", + titleAttr: _("Export or print"), + text: ' ' + _("Export") + '', + buttons: export_buttons + } + ); + + if ( add_filters ) { + // Duplicate the table header row for columnFilter + thead_row = this.find('thead tr'); + clone = thead_row.clone().addClass('filters_row'); + clone.find("th.NoSort").html(''); + thead_row.before(clone); + } + + $(".dt_button_clear_filter, .columns_controls, .export_controls").tooltip(); + return $(this).dataTable(settings); }; diff --git a/koha-tmpl/opac-tmpl/bootstrap/js/datatables.js b/koha-tmpl/opac-tmpl/bootstrap/js/datatables.js index 047fa4c438..19f2944dde 100644 --- a/koha-tmpl/opac-tmpl/bootstrap/js/datatables.js +++ b/koha-tmpl/opac-tmpl/bootstrap/js/datatables.js @@ -1,49 +1,307 @@ // These default options are for translation but can be used // for any other datatables settings -// MSG_DT_* variables comes from datatables.inc // To use it, write: // $("#table_id").dataTable($.extend(true, {}, dataTableDefaults, { // // other settings // } ) ); var dataTablesDefaults = { - "language": { - "paginate": { - "first" : window.MSG_DT_FIRST || "First", - "last" : window.MSG_DT_LAST || "Last", - "next" : window.MSG_DT_NEXT || "Next", - "previous" : window.MSG_DT_PREVIOUS || "Previous" + "oLanguage": { + "oPaginate": { + "sFirst" : __('First'), + "sLast" : __('Last'), + "sNext" : __('Next'), + "sPrevious" : __('Previous'), }, - "emptyTable" : window.MSG_DT_EMPTY_TABLE || "No data available in table", - "info" : window.MSG_DT_INFO || "Showing _START_ to _END_ of _TOTAL_ entries", - "infoEmpty" : window.MSG_DT_INFO_EMPTY || "No entries to show", - "infoFiltered" : window.MSG_DT_INFO_FILTERED || "(filtered from _MAX_ total entries)", - "lengthMenu" : window.MSG_DT_LENGTH_MENU || "Show _MENU_ entries", - "loadingRecords" : window.MSG_DT_LOADING_RECORDS || "Loading...", - "processing" : window.MSG_DT_PROCESSING || "Processing...", - "search" : window.MSG_DT_SEARCH || "Search:", - "zeroRecords" : window.MSG_DT_ZERO_RECORDS || "No matching records found" + "sEmptyTable" : __('No data available in table'), + "sInfo" : __('Showing _START_ to _END_ of _TOTAL_ entries'), + "sInfoEmpty" : __('No entries to show'), + "sInfoFiltered" : __('(filtered from _MAX_ total entries)'), + "sLengthMenu" : __('Show _MENU_ entries'), + "sLoadingRecords" : __('Loading...'), + "sProcessing" : __('Processing...'), + "sSearch" : __('Search:'), + "sZeroRecords" : __('No matching records found'), + buttons: { + "copyTitle" : __('Copy to clipboard'), + "copyKeys" : __('Press ctrl or + C to copy the table data
to your system clipboard.

To cancel, click this message or press escape.'), + "copySuccess": { + _: __('Copied %d rows to clipboard'), + 1: __('Copied one row to clipboard'), + } + } }, - // "sorting": [$(" - select row position of th -")], - "dom": 't', - "paginate": false, - // "fnHeaderCallback": function() { - // return $('th.sorting.nosort,th.sorting_desc.nosort,th.sorting_asc.nosort').removeClass("sorting sorting_desc sorting_asc").unbind("click"); - // } + "dom": '<"top pager"<"table_entries"ilp><"table_controls"fB>>tr<"bottom pager"ip>', + "buttons": [{ + fade: 100, + className: "dt_button_clear_filter", + titleAttr: __('Clear filter'), + enabled: false, + text: ' ' + __('Clear filter') + '', + available: function ( dt ) { + // The "clear filter" button is made available if this test returns true + if( dt.settings()[0].aanFeatures.f ){ // aanFeatures.f is null if there is no search form + return true; + } + }, + action: function ( e, dt, node ) { + dt.search( "" ).draw("page"); + node.addClass("disabled"); + } + }], + "aLengthMenu": [[10, 20, 50, 100, -1], [10, 20, 50, 100, __('All')]], + "iDisplayLength": 20, + initComplete: function( settings) { + var tableId = settings.nTable.id + // When the DataTables search function is triggered, + // enable or disable the "Clear filter" button based on + // the presence of a search string + $("#" + tableId ).on( 'search.dt', function ( e, settings ) { + if( settings.oPreviousSearch.sSearch == "" ){ + $("#" + tableId + "_wrapper").find(".dt_button_clear_filter").addClass("disabled"); + } else { + $("#" + tableId + "_wrapper").find(".dt_button_clear_filter").removeClass("disabled"); + } + }); + } }; + +// Return an array of string containing the values of a particular column +$.fn.dataTableExt.oApi.fnGetColumnData = function ( oSettings, iColumn, bUnique, bFiltered, bIgnoreEmpty ) { + // check that we have a column id + if ( typeof iColumn == "undefined" ) return new Array(); + // by default we only wany unique data + if ( typeof bUnique == "undefined" ) bUnique = true; + // by default we do want to only look at filtered data + if ( typeof bFiltered == "undefined" ) bFiltered = true; + // by default we do not wany to include empty values + if ( typeof bIgnoreEmpty == "undefined" ) bIgnoreEmpty = true; + // list of rows which we're going to loop through + var aiRows; + // use only filtered rows + if (bFiltered == true) aiRows = oSettings.aiDisplay; + // use all rows + else aiRows = oSettings.aiDisplayMaster; // all row numbers + + // set up data array + var asResultData = new Array(); + for (var i=0,c=aiRows.length; i -1) continue; + // else push the value onto the result data array + else asResultData.push(sValue); + } + return asResultData; +} + +// List of unbind keys (Ctrl, Alt, Direction keys, etc.) +// These keys must not launch filtering +var blacklist_keys = new Array(0, 16, 17, 18, 37, 38, 39, 40); + +// Set a filtering delay for global search field +jQuery.fn.dataTableExt.oApi.fnSetFilteringDelay = function ( oSettings, iDelay ) { + /* + * Inputs: object:oSettings - dataTables settings object - automatically given + * integer:iDelay - delay in milliseconds + * Usage: $('#example').dataTable().fnSetFilteringDelay(250); + * Author: Zygimantas Berziunas (www.zygimantas.com) and Allan Jardine + * License: GPL v2 or BSD 3 point style + * Contact: zygimantas.berziunas /AT\ hotmail.com + */ + var + _that = this, + iDelay = (typeof iDelay == 'undefined') ? 250 : iDelay; + + this.each( function ( i ) { + $.fn.dataTableExt.iApiIndex = i; + var + $this = this, + oTimerId = null, + sPreviousSearch = null, + anControl = $( 'input', _that.fnSettings().aanFeatures.f ); + + anControl.unbind( 'keyup.DT' ).bind( 'keyup.DT', function(event) { + var $$this = $this; + if (blacklist_keys.indexOf(event.keyCode) != -1) { + return this; + }else if ( event.keyCode == '13' ) { + $.fn.dataTableExt.iApiIndex = i; + _that.fnFilter( $(this).val() ); + } else { + if (sPreviousSearch === null || sPreviousSearch != anControl.val()) { + window.clearTimeout(oTimerId); + sPreviousSearch = anControl.val(); + oTimerId = window.setTimeout(function() { + $.fn.dataTableExt.iApiIndex = i; + _that.fnFilter( anControl.val() ); + }, iDelay); + } + } + }); + + return this; + } ); + return this; +} + +// Add a filtering delay on general search and on all input (with a class 'filter') +jQuery.fn.dataTableExt.oApi.fnAddFilters = function ( oSettings, sClass, iDelay ) { + var table = this; + this.fnSetFilteringDelay(iDelay); + var filterTimerId = null; + $(table).find("input."+sClass).keyup(function(event) { + if (blacklist_keys.indexOf(event.keyCode) != -1) { + return this; + }else if ( event.keyCode == '13' ) { + table.fnFilter( $(this).val(), $(this).attr('data-column_num') ); + } else { + window.clearTimeout(filterTimerId); + var input = this; + filterTimerId = window.setTimeout(function() { + table.fnFilter($(input).val(), $(input).attr('data-column_num')); + }, iDelay); + } + }); + $(table).find("select."+sClass).on('change', function() { + table.fnFilter($(this).val(), $(this).attr('data-column_num')); + }); +} + +// Sorting on html contains +// bar sort on 'bar' +function dt_overwrite_html_sorting_localeCompare() { + jQuery.fn.dataTableExt.oSort['html-asc'] = function(a,b) { + a = a.replace(/<.*?>/g, "").replace(/\s+/g, " "); + b = b.replace(/<.*?>/g, "").replace(/\s+/g, " "); + if (typeof(a.localeCompare == "function")) { + return a.localeCompare(b); + } else { + return (a > b) ? 1 : ((a < b) ? -1 : 0); + } + }; + + jQuery.fn.dataTableExt.oSort['html-desc'] = function(a,b) { + a = a.replace(/<.*?>/g, "").replace(/\s+/g, " "); + b = b.replace(/<.*?>/g, "").replace(/\s+/g, " "); + if(typeof(b.localeCompare == "function")) { + return b.localeCompare(a); + } else { + return (b > a) ? 1 : ((b < a) ? -1 : 0); + } + }; + + jQuery.fn.dataTableExt.oSort['num-html-asc'] = function(a,b) { + var x = a.replace( /<.*?>/g, "" ); + var y = b.replace( /<.*?>/g, "" ); + x = parseFloat( x ); + y = parseFloat( y ); + return ((x < y) ? -1 : ((x > y) ? 1 : 0)); + }; + + jQuery.fn.dataTableExt.oSort['num-html-desc'] = function(a,b) { + var x = a.replace( /<.*?>/g, "" ); + var y = b.replace( /<.*?>/g, "" ); + x = parseFloat( x ); + y = parseFloat( y ); + return ((x < y) ? 1 : ((x > y) ? -1 : 0)); + }; +} + +$.fn.dataTableExt.oSort['num-html-asc'] = function(a,b) { + var x = a.replace( /<.*?>/g, "" ); + var y = b.replace( /<.*?>/g, "" ); + x = parseFloat( x ); + y = parseFloat( y ); + return ((x < y) ? -1 : ((x > y) ? 1 : 0)); +}; + +$.fn.dataTableExt.oSort['num-html-desc'] = function(a,b) { + var x = a.replace( /<.*?>/g, "" ); + var y = b.replace( /<.*?>/g, "" ); + x = parseFloat( x ); + y = parseFloat( y ); + return ((x < y) ? 1 : ((x > y) ? -1 : 0)); +}; + +(function() { + +/* + * Natural Sort algorithm for Javascript - Version 0.7 - Released under MIT license + * Author: Jim Palmer (based on chunking idea from Dave Koelle) + * Contributors: Mike Grier (mgrier.com), Clint Priest, Kyle Adams, guillermo + * See: http://js-naturalsort.googlecode.com/svn/trunk/naturalSort.js + */ +function naturalSort (a, b) { + var re = /(^-?[0-9]+(\.?[0-9]*)[df]?e?[0-9]?$|^0x[0-9a-f]+$|[0-9]+)/gi, + sre = /(^[ ]*|[ ]*$)/g, + dre = /(^([\w ]+,?[\w ]+)?[\w ]+,?[\w ]+\d+:\d+(:\d+)?[\w ]?|^\d{1,4}[\/\-]\d{1,4}[\/\-]\d{1,4}|^\w+, \w+ \d+, \d{4})/, + hre = /^0x[0-9a-f]+$/i, + ore = /^0/, + // convert all to strings and trim() + x = a.toString().replace(sre, '') || '', + y = b.toString().replace(sre, '') || '', + // chunk/tokenize + xN = x.replace(re, '\0$1\0').replace(/\0$/,'').replace(/^\0/,'').split('\0'), + yN = y.replace(re, '\0$1\0').replace(/\0$/,'').replace(/^\0/,'').split('\0'), + // numeric, hex or date detection + xD = parseInt(x.match(hre), 10) || (xN.length != 1 && x.match(dre) && Date.parse(x)), + yD = parseInt(y.match(hre), 10) || xD && y.match(dre) && Date.parse(y) || null; + // first try and sort Hex codes or Dates + if (yD) + if ( xD < yD ) return -1; + else if ( xD > yD ) return 1; + // natural sorting through split numeric strings and default strings + for(var cLoc=0, numS=Math.max(xN.length, yN.length); cLoc < numS; cLoc++) { + // find floats not starting with '0', string or 0 if not defined (Clint Priest) + var oFxNcL = !(xN[cLoc] || '').match(ore) && parseFloat(xN[cLoc]) || xN[cLoc] || 0; + var oFyNcL = !(yN[cLoc] || '').match(ore) && parseFloat(yN[cLoc]) || yN[cLoc] || 0; + // handle numeric vs string comparison - number < string - (Kyle Adams) + if (isNaN(oFxNcL) !== isNaN(oFyNcL)) return (isNaN(oFxNcL)) ? 1 : -1; + // rely on string comparison if different types - i.e. '02' < 2 != '02' < '2' + else if (typeof oFxNcL !== typeof oFyNcL) { + oFxNcL += ''; + oFyNcL += ''; + } + if (oFxNcL < oFyNcL) return -1; + if (oFxNcL > oFyNcL) return 1; + } + return 0; +} + +jQuery.extend( jQuery.fn.dataTableExt.oSort, { + "natural-asc": function ( a, b ) { + return naturalSort(a,b); + }, + + "natural-desc": function ( a, b ) { + return naturalSort(a,b) * -1; + } +} ); + +}()); + /* Plugin to allow sorting on data stored in a span's title attribute * * Ex: [% formatted_date %] * * In DataTables config: - * "columns": [ - * { "type": "title-string" }, + * "aoColumns": [ + * { "sType": "title-string" }, * ] * http://datatables.net/plug-ins/sorting#hidden_title_string */ -jQuery.extend( jQuery.fn.dataTableExt.sort, { +jQuery.extend( jQuery.fn.dataTableExt.oSort, { "title-string-pre": function ( a ) { - return a.match(/title="(.*?)"/)[1].toLowerCase(); + var m = a.match(/title="(.*?)"/); + if ( null !== m && m.length ) { + return m[1].toLowerCase(); + } + return ""; }, "title-string-asc": function ( a, b ) { @@ -55,17 +313,19 @@ jQuery.extend( jQuery.fn.dataTableExt.sort, { } } ); -/* Plugin to allow sorting numerically on data stored in a span's title attribute +/* Plugin to allow sorting on numeric data stored in a span's title attribute * - * Ex: Total: [% total %] + * Ex: + * [% formatted currency %] + * * * In DataTables config: - * "columns": [ - * { "type": "title-numeric" } - * ] - * http://legacy.datatables.net/plug-ins/sorting#hidden_title + * "aoColumns": [ + * { "sType": "title-numeric" }, + * ] + * http://datatables.net/plug-ins/sorting#hidden_title */ -jQuery.extend( jQuery.fn.dataTableExt.sort, { +jQuery.extend( jQuery.fn.dataTableExt.oSort, { "title-numeric-pre": function ( a ) { var x = a.match(/title="*(-?[0-9\.]+)/)[1]; return parseFloat( x ); @@ -85,8 +345,8 @@ jQuery.extend( jQuery.fn.dataTableExt.sort, { /* Plugin to allow text sorting to ignore articles * * In DataTables config: - * "columns": [ - * { "type": "anti-the" }, + * "aoColumns": [ + * { "sType": "anti-the" }, * ] * Based on the plugin found here: * http://datatables.net/plug-ins/sorting#anti_the @@ -95,8 +355,9 @@ jQuery.extend( jQuery.fn.dataTableExt.sort, { * from a configuration file (in English, "a," "an," and "the") */ - if(CONFIG_EXCLUDE_ARTICLES_FROM_SORT){ - var articles = CONFIG_EXCLUDE_ARTICLES_FROM_SORT.split(" "); + var config_exclude_articles_from_sort = __('a an the'); + if (config_exclude_articles_from_sort){ + var articles = config_exclude_articles_from_sort.split(" "); var rpattern = ""; for(i=0;i/g, "" ); var y = x.trim(); @@ -124,13 +385,131 @@ jQuery.extend( jQuery.fn.dataTableExt.sort, { }()); +// Remove string between NSB NSB characters +$.fn.dataTableExt.oSort['nsb-nse-asc'] = function(a,b) { + var pattern = new RegExp("\x88.*\x89"); + a = a.replace(pattern, ""); + b = b.replace(pattern, ""); + return (a > b) ? 1 : ((a < b) ? -1 : 0); +} +$.fn.dataTableExt.oSort['nsb-nse-desc'] = function(a,b) { + var pattern = new RegExp("\x88.*\x89"); + a = a.replace(pattern, ""); + b = b.replace(pattern, ""); + return (b > a) ? 1 : ((b < a) ? -1 : 0); +} + +/* Define two custom functions (asc and desc) for basket callnumber sorting */ +jQuery.fn.dataTableExt.oSort['callnumbers-asc'] = function(x,y) { + var x_array = x.split("
"); + var y_array = y.split("
"); + + /* Pop the first elements, they are empty strings */ + x_array.shift(); + y_array.shift(); + + x_array = jQuery.map( x_array, function( a ) { + return parse_callnumber( a ); + }); + y_array = jQuery.map( y_array, function( a ) { + return parse_callnumber( a ); + }); + + x_array.sort(); + y_array.sort(); + + x = x_array.shift(); + y = y_array.shift(); + + if ( !x ) { x = ""; } + if ( !y ) { y = ""; } + + return ((x < y) ? -1 : ((x > y) ? 1 : 0)); +}; + +jQuery.fn.dataTableExt.oSort['callnumbers-desc'] = function(x,y) { + var x_array = x.split("
"); + var y_array = y.split("
"); + + /* Pop the first elements, they are empty strings */ + x_array.shift(); + y_array.shift(); + + x_array = jQuery.map( x_array, function( a ) { + return parse_callnumber( a ); + }); + y_array = jQuery.map( y_array, function( a ) { + return parse_callnumber( a ); + }); + + x_array.sort(); + y_array.sort(); + + x = x_array.pop(); + y = y_array.pop(); + + if ( !x ) { x = ""; } + if ( !y ) { y = ""; } + + return ((x < y) ? 1 : ((x > y) ? -1 : 0)); +}; + +function parse_callnumber ( html ) { + var array = html.split(''); + if ( array[1] ) { + array = array[1].split(''); + return array[0]; + } else { + return ""; + } +} + +// see http://www.datatables.net/examples/advanced_init/footer_callback.html +function footer_column_sum( api, column_numbers ) { + // Remove the formatting to get integer data for summation + var intVal = function ( i ) { + if ( typeof i === 'number' ) { + if ( isNaN(i) ) return 0; + return i; + } else if ( typeof i === 'string' ) { + var value = i.replace(/[a-zA-Z ,.]/g, '')*1; + if ( isNaN(value) ) return 0; + return value; + } + return 0; + }; + + + for ( var indice = 0 ; indice < column_numbers.length ; indice++ ) { + var column_number = column_numbers[indice]; + + var total = 0; + var cells = api.column( column_number, { page: 'current' } ).nodes().to$().find("span.total_amount"); + $(cells).each(function(){ + total += intVal( $(this).html() ); + }); + total /= 100; // Hard-coded decimal precision + + // Update footer + $( api.column( column_number ).footer() ).html(total.format_price()); + }; +} + +function filterDataTable( table, column, term ){ + if( column ){ + table.column( column ).search( term ).draw("page"); + } else { + table.search( term ).draw("page"); + } +} + jQuery.fn.dataTable.ext.errMode = function(settings, note, message) { console.warn(message); }; (function($) { - $.fn.api = function(options) { + $.fn.api = function(options, columns_settings, add_filters) { var settings = null; if(options) { if(!options.criteria || ['contains', 'starts_with', 'ends_with', 'exact'].indexOf(options.criteria.toLowerCase()) === -1) options.criteria = 'contains'; @@ -140,6 +519,10 @@ jQuery.fn.dataTable.ext.errMode = function(settings, note, message) { "paging": true, 'serverSide': true, 'searching': true, + 'processing': true, + 'language': { + 'emptyTable': (options.emptyTable) ? options.emptyTable : "No data available in table" + }, 'pagingType': 'full', 'ajax': { 'type': 'GET', @@ -213,7 +596,132 @@ jQuery.fn.dataTable.ext.errMode = function(settings, note, message) { } }, options); } + + var counter = 0; + var hidden_ids = []; + var included_ids = []; + + $(columns_settings).each( function() { + var named_id = $( 'thead th[data-colname="' + this.columnname + '"]', this ).index( 'th' ); + var used_id = settings.bKohaColumnsUseNames ? named_id : counter; + if ( used_id == -1 ) return; + + if ( this['is_hidden'] == "1" ) { + hidden_ids.push( used_id ); + } + if ( this['cannot_be_toggled'] == "0" ) { + included_ids.push( used_id ); + } + counter++; + }); + + var exportColumns = ":visible:not(.noExport)"; + if( settings.hasOwnProperty("exportColumns") ){ + // A custom buttons configuration has been passed from the page + exportColumns = settings["exportColumns"]; + } + + var export_format = { + body: function ( data, row, column, node ) { + var newnode = $(node); + + if ( newnode.find(".noExport").length > 0 ) { + newnode = newnode.clone(); + newnode.find(".noExport").remove(); + } + + return newnode.text().replace( /\n/g, ' ' ).trim(); + } + } + + var export_buttons = [ + { + extend: 'excelHtml5', + text: _("Excel"), + exportOptions: { + columns: exportColumns, + format: export_format + }, + }, + { + extend: 'csvHtml5', + text: _("CSV"), + exportOptions: { + columns: exportColumns, + format: export_format + }, + }, + { + extend: 'copyHtml5', + text: _("Copy"), + exportOptions: { + columns: exportColumns, + format: export_format + }, + }, + { + extend: 'print', + text: _("Print"), + exportOptions: { + columns: exportColumns, + format: export_format + }, + } + ]; + + settings[ "buttons" ] = [ + { + fade: 100, + className: "dt_button_clear_filter", + titleAttr: _("Clear filter"), + enabled: false, + text: ' ' + _("Clear filter") + '', + action: function ( e, dt, node, config ) { + dt.search( "" ).draw("page"); + node.addClass("disabled"); + } + } + ]; + + if( included_ids.length > 0 ){ + settings[ "buttons" ].push( + { + extend: 'colvis', + fade: 100, + columns: included_ids, + className: "columns_controls", + titleAttr: _("Columns settings"), + text: ' ' + _("Columns") + '', + exportOptions: { + columns: exportColumns + } + } + ); + } + + settings[ "buttons" ].push( + { + extend: 'collection', + autoClose: true, + fade: 100, + className: "export_controls", + titleAttr: _("Export or print"), + text: ' ' + _("Export") + '', + buttons: export_buttons + } + ); + + if ( add_filters ) { + // Duplicate the table header row for columnFilter + thead_row = this.find('thead tr'); + clone = thead_row.clone().addClass('filters_row'); + clone.find("th.NoSort").html(''); + thead_row.before(clone); + } + + $(".dt_button_clear_filter, .columns_controls, .export_controls").tooltip(); + return $(this).dataTable(settings); }; -})(jQuery); \ No newline at end of file +})(jQuery); -- 2.26.2