Issues (994)

libs/datatables.hideEmptyColums.js (2 issues)

Severity
1
/**
2
 * @summary     HideEmptyColumns
3
 * @description Hide any (or specified) columns if no cells in the column(s)
4
 *              are populated with any values
5
 * @version     1.2.1
6
 * @file        dataTables.hideEmptyColumns.js
7
 * @author      Justin Hyland (http://www.justinhyland.com)
8
 * @contact     [email protected]
9
 * @copyright   Copyright 2015 Justin Hyland
10
 * @url         https://github.com/jhyland87/DataTables-Hide-Empty-Columns
11
 *
12
 * License      MIT - http://datatables.net/license/mit
13
 *
14
 * Set the column visibility to hidden for any targeted columns that contain nothing
15
 * but null or empty values.
16
 *
17
 *
18
 * Parameters:
19
 *
20
 * -------------
21
 * hideEmptyCols
22
 *      Required:			true
23
 *      Type:				boolean|array|object
24
 *      Aliases:            hideEmptyColumns
25
 *      Description:		Primary setting, either target all columns, or specify an array for a list of cols, or an
26
 *                          object for advanced settings
27
 *      Examples:           hideEmptyCols: true
28
 *                          hideEmptyCols: [ 0, 2, 'age' ]
29
 *                          hideEmptyCols: { columns: [ 0, 2, 'age' ] }
30
 *                          hideEmptyCols: { columns: true }
31
 *
32
 * hideEmptyCols.columns
33
 *      Required:           false
34
 *      Type:               boolean|array
35
 *      Description:        Either true for all columns, or an array of indexes or dataSources
36
 *      Examples:           [ 0, 2, 'age' ]  // Column indexes 0 and 2, and the column name 'age'
37
 *                          true  // All columns
38
 *
39
 * hideEmptyCols.whiteList
40
 *      Required:           false
41
 *      Type:               boolean
42
 *      Default:            true
43
 *      Description:        Specify if the column list is to be targeted, or excluded
44
 *
45
 * hideEmptyCols.trim
46
 *      Required:           false
47
 *      Type:               boolean
48
 *      Default:            true
49
 *      Description:        Determines if column data values should be trimmed before checked for empty values
50
 *
51
 * hideEmptyCols.emptyVals
52
 *      Required:           false
53
 *      Type:               string|number|array|regex
54
 *      Description:        Define one or more values that will be interpreted as "empty"
55
 *      Examples:           [ '<br>', '</br>', '<BR>', '</BR>', '&nbsp;' ]  // HTML Line breaks, and the HTML NBSP char
56
 *                          /\<\/?br\>/i    // Any possible HTML line break character that matches this pattern
57
 *                          ['false', '0', /\<\/?br\>/i]   // The string values 'false' and '0', and all HTML breaks
58
 *
59
 * hideEmptyCols.onStateLoad
60
 *      Required:           false
61
 *      Type:               boolean
62
 *      Default:            true
63
 *      Description:        Determines if the main _checkColumns function should execute after the DT state is loaded
64
 *                          (when the DT stateSave option is enabled). This function will override the column visibility
65
 *                          state in stateSave
66
 *
67
 * hideEmptyCols.perPage
68
 *      Required:           false
69
 *      Type:               boolean
70
 *      Description:        Determine if columns should only be hidden if it has no values on the current page
71
 *
72
 *
73
 * @example
74
 *    // Target all columns - Hide any columns that contain all null/empty values
75
 *    $('#example').DataTable({
76
 *        hideEmptyCols: true
77
 *    })
78
 *
79
 * @example
80
 *    // Target the column indexes 0 & 2
81
 *    $('#example').DataTable({
82
 *        hideEmptyCols: [0,2]
83
 *    })
84
 *
85
 * @example
86
 *    // Target the column with 'age' data source
87
 *    $('#example').DataTable({
88
 *        ajax: 'something.js',
89
 *        hideEmptyCols: ['age'],
90
 *        buttons: [ 'columnsToggle' ],
91
 *        columns: [
92
 *            { name: 'name',     data: 'name' },
93
 *            { name: 'position', data: 'position' },
94
 *            { name: 'age',      data: 'age' }
95
 *        ]
96
 *    })
97
 *
98
 * @example
99
 *    // Target everything *except* the columns 1, 2 & 3
100
 *    $('#example').DataTable({
101
 *        hideEmptyCols: {
102
 *              columns: [ 1, 2, 3 ],
103
 *              whiteList: false
104
 *        }
105
 *    })
106
 *
107
 * @example
108
 *    // Target column indexes 1 and 4, adding custom empty values, and only hide the column if empty on current page
109
 *    $('#example').DataTable({
110
 *        hideEmptyCols: {
111
 *              columns: [ 1, 4 ],
112
 *              perPage: true,
113
 *              emptyVals: [ '0', /(no|false|disabled)/i ]
114
 *        }
115
 *    })
116
 */
117
//"use strict";
118
(function(window, document, $) {
119
  // On DT Initialization
120
  $(document).on('init.dt', function(e, dtSettings) {
121
    if (e.namespace !== 'dt')
122
      return
123
124
    // Check for either hideEmptyCols or hideEmptyColumns
125
    var options = dtSettings.oInit.hideEmptyCols || dtSettings.oInit.hideEmptyColumns
126
127
    // If neither of the above settings are found, then call it quits
128
    if (!options)
129
      return
130
131
    // Helper function to get the value of a config item
132
    var _cfgItem = function(item, def) {
133
      if ($.isPlainObject(options) && typeof options[item] !== 'undefined')
134
        return options[item]
135
136
      return def
137
    }
138
139
    // Gather all the setting values which will be used
140
    var api = new $.fn.dataTable.Api(dtSettings),
141
      emptyCount = 0,
142
      colList = [],
143
      isWhiteList = !_cfgItem('whiteList', false),
144
      perPage = _cfgItem('perPage'),
145
      trimData = _cfgItem('trim', true),
146
      onStateLoad = _cfgItem('onStateLoad', true)
147
148
    // Helper function to determine if a cell is empty (including processing custom empty values)
149
    var _isEmpty = function(colData) {
150
      // Trim the data (unless its set to false)
151
      if (trimData)
152
        colData = $.trim(colData)
153
154
      // Basic check
155
      if (colData === null || colData.length === 0)
156
        return true
157
158
      // Default to false, any empty matches will reset to true
159
      var retVal = false
160
161
      var emptyVals = _cfgItem('emptyVals')
162
163
      // Internal helper function to check the value against a custom defined empty value (which can be a
164
      // regex pattern or a simple string)
165
      var _checkEmpty = function(val, emptyVal) {
166
        var objType = Object.prototype.toString.call(emptyVal)
167
168
        var match = objType.match(/^\[object\s(.*)\]$/)
169
170
        // If its a regex pattern, then handle it differently
171
        if (match[1] === 'RegExp')
172
          return val.match(emptyVal)
173
174
        // Note: Should this comparison maybe use a lenient/loose comparison operator? hmm..
175
        return val === emptyVal
176
      }
177
178
      // If multiple custom empty values are defined in an array, then check each
179
      if ($.isArray(emptyVals)) {
180
        $.each(emptyVals, function(i, ev) {
181
          if (_checkEmpty(colData, ev))
182
            retVal = true
183
        })
184
      }
185
186
      // Otherwise, just check the one, if set
187
      else if (typeof emptyVals !== 'undefined') {
188
        if (_checkEmpty(colData, emptyVals))
189
          retVal = true
190
      }
191
192
      return retVal
193
    }
194
195
    // If the hideEmptyCols setting is an Array (of column indexes to target)
196
    if ($.isArray(options)) {
197
      // And its populated..
198
      if (options.length !== 0) {
199
        $.each(options, function(k, i) {
200
          // Try to get the real column index from whatever was configured
201
          var indx = api.column(i).index()
202
203
          colList.push(typeof indx !== 'undefined' ? indx : i)
204
        })
205
      } else {
206
        // Otherwise, quit! since its just an empty array
207
        return
208
      }
209
    }
210
211
    // If hideEmptyCols setting is an Object (of plugin settings)
212
    else if ($.isPlainObject(options)) {
213
      // If options.columns isnt specifically
214
      if (typeof options.columns === 'undefined' || options.columns === true) {
215
        // Set colList to true, enabling every column as a target
216
        colList = api.columns().indexes().toArray()
217
      }
218
219
      // If its an array, then it should contain the column indexs, so use that
220
      else if ($.isArray(options.columns)) {
221
        // Otherwise, set the colList
222
        colList = options.columns
223
      }
224
225
      // If `options.columns` isn't an array (of indexes) or a boolean (disable/enable all columns),
226
      // then throw a hissy fit
227
      else if (typeof options.columns !== 'boolean') {
228
        console.error('[Hide Empty Columns]: Expected typeof `columns` setting value to be an array, boolean or undefined, but received value type "%s"', typeof options.columns)
229
        return
230
      }
231
232
      // The only thing left could be if its false, so just stop all together
233
      else {
234
        return
235
      }
236
    }
237
238
    // If its just a basic 'true' targeting all columns..
239
    else if (options === true) {
240
      // .. Then get the list of all column indexes
241
      colList = api.columns().indexes().toArray()
242
    }
243
244
    // Anything else should just go away
245
    else {
246
      return
247
    }
248
249
    // Function to check the column rows
250
    var _checkColumns = function() {
251
      var info = api.page.info(),
252
        colFilter = (perPage ? {
253
          search: 'applied'
254
        } : undefined)
255
256
      // Iterate through the table, column by column
257
      //api.columns({ search: 'applied' }).every(function () {
258
      api.columns(colFilter).every(function() {
259
        emptyCount = 0
0 ignored issues
show
The variable emptyCount seems to be never used. Consider removing it.
Loading history...
260
261
        // If the current column is *not* found in the list..
262
        if ($.inArray(this.index(), colList) === -1 // Check column index #
263
          &&
264
          $.inArray(api.column(this.index()).dataSrc(), colList) === -1) // Check column name (dataSrc)
265
        {
266
          // .. And the list type is whitelist, then skip this loop
267
          if (isWhiteList === true) return
268
        }
269
        // If the current column *is* found in the list..
270
        else {
271
          // .. And the list type is blacklist, then skip this loop
272
          if (isWhiteList === false) return
273
        }
274
275
        // This gets ALL data in current column.. Need just the visible rows
276
        var data = this.data().toArray(),
277
          isVis = false,
278
          intStart = (perPage === true && info.serverSide === false ? info.start : 0),
279
          intStop = (perPage === true && info.serverSide === false ? info.end : data.length),
280
          dtState = api.state.loaded()
0 ignored issues
show
The variable dtState seems to be never used. Consider removing it.
Loading history...
281
282
        //for( var i = 0; i < data.length; i ++ ) {
283
        for (var i = intStart; i < intStop; i++) {
284
          if (!_isEmpty(data[i])) {
285
            isVis = true
286
            break
287
          }
288
        }
289
290
        // If the # of empty is the same as the length, then no values in col were found
291
        api.column(this.index()).visible(isVis)
292
293
      })
294
    }
295
296
    // If stateSave is enabled in this DT instance, then toggle the column visibility afterwords
297
    if (onStateLoad === true)
298
      api.on('stateLoadParams.dt', _checkColumns)
299
300
    // If were checking for each page, then attach functions to any events that may introduce or remove new
301
    // columns/rows from the table (page, order, search and length)
302
    if (perPage === true)
303
      api
304
      .on('page.dt', _checkColumns)
305
      .on('search.dt', _checkColumns)
306
      .on('order.dt', _checkColumns)
307
      .on('length.dt', _checkColumns)
308
      .on('draw.dt', _checkColumns) // triggers after data loaded with AJAX
309
310
    // Run check for the initial page load
311
    _checkColumns()
312
  })
313
})(window, document, jQuery)