1
0
Fork 0
mirror of https://github.com/koalyptus/TableFilter.git synced 2024-06-02 14:02:31 +02:00

Compare commits

..

No commits in common. "master" and "0.6.70" have entirely different histories.

56 changed files with 6939 additions and 6262 deletions

View file

@ -14,7 +14,7 @@
"keyword-spacing": ["error", { "after": true, "before": true }],
"max-depth": [2, 7],
"max-statements": [2, 133],
"complexity": [2, 45],
"complexity": [2, 41],
"no-unused-vars": 2,
"no-eval": 2,
"no-underscore-dangle": 0,

View file

@ -1,38 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**Reproduction steps:**
Steps to reproduce the behavior:
1. [First Step]
2. [Second Step]
3. [Other Steps...]
**Expected behavior**
A clear and concise description of what you expected to happen.
**Observed behavior:**
A clear and concise description of observed behavior.
**Screenshots**
If applicable, add screenshots to help explain your problem.
![Screenshots and GIFs which follow reproduction steps to demonstrate the problem](url)
**TableFilter version:** [Enter TableFilter version here]
**Browser and version:** [Enter Browser name and version here]
**OS and version:** [Enter OS name and version here]
**Device:** [e.g. iPhone6]
**Additional information/context:**
Add any other context about the problem here.
* Problem started happening recently, didn't happen in an older version of TableFilter: [Yes/No] if yes [version here]
* Problem can be reliably reproduced, doesn't happen randomly: [Yes/No]

View file

@ -1,36 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Enhancement/feature description**
[A clear and concise description of what the problem is. Ex. I'm always frustrated when ...]
**Steps which explain the enhancement/feature**
1. [First Step]
2. [Second Step]
3. [Other Steps...]
**Current and suggested behavior**
[Describe current and suggested behavior here]
**Why would the enhancement be useful to most users**
[Explain why the enhancement would be useful to most users]
**Screenshots and GIFs**
![Screenshots and GIFs which demonstrate the steps or part of TableFilter the enhancement suggestion is related to](url)
**Additional context**
[Add any other context or screenshots about the feature request here.]
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**TableFilter Version:** [Enter TableFilter version here]
**Browser and version:** [Enter Browser name and version here]
**OS and version:** [Enter OS name and version here]
**Device:** [e.g. iPhone6]

View file

@ -1,17 +0,0 @@
---
name: Support
about: I need support with TableFilter
title: ''
labels: ''
assignees: ''
---
For usage and support questions, please check out resources below, you might find an answer:
- https://www.tablefilter.com/
- https://www.tablefilter.com/docs/
- https://www.tablefilter.com/examples.html
- https://github.com/koalyptus/TableFilter/wiki/
- https://github.com/koalyptus/TableFilter/issues?q=is%3Aissue+is%3Aclosed
- https://codepen.io/koalyptus/pens/public/

View file

@ -13,7 +13,7 @@ module.exports = function (grunt) {
coverage: {
disposeCollector: true,
src: ['dist/tablefilter/*.js'],
instrumentedFiles: 'report/temp/',
instrumentedFiles: 'temp/',
htmlReport: 'report/coverage',
coberturaReport: 'report/',
lcovReport: 'report/',

View file

@ -24,7 +24,7 @@ users to filter and limit the data displayed within a long table. By default, th
* Attach to an existing HTML table
* Integration with any server-side technology as this is a pure client-side
solution
* Exhaustive documentation and powerful API
* Exhaustive documentation and poweful API
## Getting started
* Clone the repo using Git:

4
dist/starter.html vendored
View file

@ -1,10 +1,10 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>tablefilter v0.7.3 - Starter</title>
<title>tablefilter v0.6.65 - Starter</title>
</head>
<body>
<h1>tablefilter v0.7.3</h1>
<h1>tablefilter v0.6.65</h1>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
dist/tablefilter/tablefilter.js.map vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

12231
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
{
"name": "tablefilter",
"version": "0.7.3",
"version": "0.6.70",
"description": "A Javascript library making HTML tables filterable and a bit more",
"license": "MIT",
"author": {
@ -40,17 +40,17 @@
"tag": "next"
},
"devDependencies": {
"@babel/core": "7.10.0",
"@babel/preset-env": "7.10.0",
"babel-eslint": "10.1.0",
"@babel/core": "7.2.2",
"@babel/preset-env": "7.2.0",
"babel-eslint": "10.0.0",
"babel-loader": "^8.0.2",
"babel-preset-env": "1.7.0",
"clean-webpack-plugin": "^3.0.0",
"codecov": "3.7.1",
"clean-webpack-plugin": "^1.0.0",
"codecov": "3.1.0",
"diacritics": "1.3.0",
"esdoc": "1.1.0",
"esdoc-standard-plugin": "1.0.0",
"eslint": "6.5.0",
"eslint": "5.0.1",
"format-number": "3.0.0",
"grunt": "^1.0.1",
"grunt-cli": "1.3.2",
@ -58,18 +58,18 @@
"grunt-contrib-connect": "^2.0.0",
"grunt-contrib-copy": "^1.0.0",
"grunt-contrib-watch": "^1.0.0",
"grunt-qunit-istanbul": "1.1.0",
"grunt-shell": "3.0.0",
"grunt-qunit-istanbul": "1.0.0",
"grunt-shell": "2.1.0",
"grunt-string-replace": "^1.3.1",
"isparta-loader": "2.0.0",
"script-loader": "^0.7.0",
"string-replace-webpack-plugin": "^0.1.3",
"stylus": "^0.54.5",
"sugar-date": "2.0.6",
"uglifyjs-webpack-plugin": "2.2.0",
"webpack": "^4.38.0",
"sugar-date": "2.0.4",
"uglifyjs-webpack-plugin": "2.0.1",
"webpack": "^4.0.1",
"webpack-cli": "^3.0.8",
"webpack-dev-server": "^3.1.11"
"webpack-dev-server": "^3.0.0"
},
"dependencies": {},
"bugs": {

View file

@ -1,3 +1,19 @@
import {DateType} from './modules/dateType';
import {Help} from './modules/help';
import {State} from './modules/state';
import {GridLayout} from './modules/gridLayout';
import {Loader} from './modules/loader';
import {HighlightKeyword} from './modules/highlightKeywords';
import {PopupFilter} from './modules/popupFilter';
import {MarkActiveColumns} from './modules/markActiveColumns';
import {RowsCounter} from './modules/rowsCounter';
import {StatusBar} from './modules/statusBar';
import {ClearButton} from './modules/clearButton';
import {AlternateRows} from './modules/alternateRows';
import {NoResults} from './modules/noResults';
import {Paging} from './modules/paging';
import {Toolbar} from './modules/toolbar';
/**
* Filter types
*/
@ -116,3 +132,75 @@ export const IP_ADDRESS = 'ipaddress';
* @type {Number}
*/
export const AUTO_FILTER_DELAY = 750;
/**
* TableFilter features definitions
* @type {Object}
*/
export const FEATURES = {
dateType: {
class: DateType,
name: 'dateType'
},
help: {
class: Help,
name: 'help',
enforce: true
},
state: {
class: State,
name: 'state'
},
markActiveColumns: {
class: MarkActiveColumns,
name: 'markActiveColumns'
},
gridLayout: {
class: GridLayout,
name: 'gridLayout'
},
loader: {
class: Loader,
name: 'loader'
},
highlightKeyword: {
class: HighlightKeyword,
name: 'highlightKeyword',
property: 'highlightKeywords'
},
popupFilter: {
class: PopupFilter,
name: 'popupFilter',
property: 'popupFilters'
},
rowsCounter: {
class: RowsCounter,
name: 'rowsCounter'
},
statusBar: {
class: StatusBar,
name: 'statusBar'
},
clearButton: {
class: ClearButton,
name: 'clearButton',
property: 'btnReset'
},
alternateRows: {
class: AlternateRows,
name: 'alternateRows'
},
noResults: {
class: NoResults,
name: 'noResults'
},
paging: {
class: Paging,
name: 'paging'
},
toolbar: {
class: Toolbar,
name: 'toolbar',
enforce: true
}
};

View file

@ -36,7 +36,7 @@ export const getFirstTextNode = (node) => {
/**
* Creates an html element with given collection of attributes
* @param {String} tag html tag name
* @param {String} tag a string of the html tag to create
* @param {Array} an undetermined number of arrays containing the with 2
* items, the attribute name and its value ['id','myId']
* @return {Object} created element

View file

@ -21,7 +21,7 @@ export default class AdapterEzEditTable extends Feature {
* @param {Object} cfg Configuration options for ezEditTable library
*/
constructor(tf, cfg) {
super(tf, AdapterEzEditTable);
super(tf, cfg.name);
/**
* Module description
@ -505,5 +505,3 @@ export default class AdapterEzEditTable extends Feature {
this.initialized = false;
}
}
AdapterEzEditTable.meta = {altName: 'advancedGrid'};

View file

@ -33,7 +33,7 @@ export default class ColOps extends Feature {
* @param {Object} opts Configuration object
*/
constructor(tf, opts) {
super(tf, ColOps);
super(tf, opts.name);
/**
* Callback fired before columns operations start

View file

@ -23,7 +23,7 @@ export default class ColsVisibility extends Feature {
* @param {Object} Configuration object
*/
constructor(tf, f) {
super(tf, ColsVisibility);
super(tf, f.name);
// Configuration object
let cfg = this.config;

View file

@ -18,7 +18,7 @@ export default class FiltersVisibility extends Feature {
* @param {Object} Configuration object
*/
constructor(tf, f) {
super(tf, FiltersVisibility);
super(tf, f.name);
/**
* Module name

View file

@ -20,7 +20,7 @@ export default class AdapterSortableTable extends Feature {
* @param {Object} opts Configuration object
*/
constructor(tf, opts) {
super(tf, AdapterSortableTable);
super(tf, opts.name);
/**
* Module name
@ -508,12 +508,9 @@ export default class AdapterSortableTable extends Feature {
}
AdapterSortableTable.meta = {altName: 'sort'};
//Converters
function ipAddress(value) {
let vals = value.split('.');
// eslint-disable-next-line no-unused-vars
for (let x in vals) {
let val = vals[x];
while (3 > val.length) {

View file

@ -1,4 +1,3 @@
import {toCamelCase} from './string';
const NOT_IMPLEMENTED = 'Not implemented.';
@ -9,11 +8,9 @@ export class Feature {
/**
* Creates an instance of Feature
* @param {Object} tf TableFilter instance
* @param {Class} feature Feature class for TableFilter registration
* @param {String} feature Feature name known by TableFilter
*/
constructor(tf, cls) {
cls.meta = cls.meta || {};
constructor(tf, feature) {
/**
* TableFilter instance
* @type {TableFilter}
@ -21,18 +18,16 @@ export class Feature {
this.tf = tf;
/**
* Feature name is the camelised class name as per TableFilter's
* convention
* Feature name
* @type {String}
*/
this.feature = cls.meta.altName || cls.meta.name
|| toCamelCase(cls.name);
this.feature = feature;
/**
* TableFilter feature setting
* @type {Boolean}
*/
this.enabled = tf[this.feature];
this.enabled = tf[feature];
/**
* TableFilter configuration

View file

@ -14,10 +14,9 @@ export class AlternateRows extends Feature {
* @param {Object} tf TableFilter instance
*/
constructor(tf) {
super(tf, AlternateRows);
super(tf, 'alternateRows');
let config = this.config;
/**
* Css class for even rows (default: 'even')
* @type {String}

View file

@ -1,7 +1,7 @@
import {Feature} from '../feature';
import {
ignoreCase, numSortAsc, numSortDesc,
dateSortAsc, dateSortDesc, sortNumberStr, sortDateStr
dateSortAsc, sortNumberStr, sortDateStr
} from '../sort';
import {isArray, isObj, isEmpty} from '../types';
import {NUMBER, FORMATTED_NUMBER, DATE} from '../const';
@ -18,8 +18,8 @@ export class BaseDropdown extends Feature {
* Creates an instance of BaseDropdown
* @param {TableFilter} tf
*/
constructor(tf, cls) {
super(tf, cls);
constructor(tf) {
super(tf, 'baseDropdown');
let f = this.config;
@ -33,7 +33,7 @@ export class BaseDropdown extends Feature {
f.filter_options_sorter :
null;
// TODO: move here all properties shared by Dropdown and CheckList
// TODO: move here all properties shared by Dropdown CheckList
/**
* Has custom options
@ -72,15 +72,14 @@ export class BaseDropdown extends Feature {
* @private
*/
sortOptions(colIndex, options = []) {
let {tf} = this;
let tf = this.tf;
if (tf.isCustomOptions(colIndex) || !tf.sortSlc ||
(isArray(tf.sortSlc) && tf.sortSlc.indexOf(colIndex) === -1)) {
return options;
}
let { caseSensitive, sortFilterOptionsDesc } = tf;
let isSortDesc = sortFilterOptionsDesc.indexOf(colIndex) !== -1;
let { caseSensitive, sortNumDesc } = tf;
let compareFn;
if (this.customSorter &&
@ -90,18 +89,18 @@ export class BaseDropdown extends Feature {
}
else if (tf.hasType(colIndex, [NUMBER, FORMATTED_NUMBER])) {
let decimal = tf.getDecimal(colIndex);
let comparer = isSortDesc ? numSortDesc : numSortAsc;
let comparer = numSortAsc;
if (sortNumDesc === true || sortNumDesc.indexOf(colIndex) !== -1) {
comparer = numSortDesc;
}
compareFn = sortNumberStr(comparer, decimal);
}
else if (tf.hasType(colIndex, [DATE])) {
let locale = tf.feature('dateType').getLocale(colIndex);
let comparer = isSortDesc ? dateSortDesc : dateSortAsc;
let comparer = dateSortAsc;
compareFn = sortDateStr(comparer, locale);
} else { // string
compareFn = caseSensitive ? undefined : ignoreCase;
if (isSortDesc) {
return options.sort(compareFn).reverse();
}
}
return options.sort(compareFn);

View file

@ -22,7 +22,7 @@ export class CheckList extends BaseDropdown {
* @param {TableFilter} tf TableFilter instance
*/
constructor(tf) {
super(tf, CheckList);
super(tf, 'checkList');
let f = this.config;

View file

@ -15,7 +15,7 @@ export class ClearButton extends Feature {
* @param {TableFilter} tf TableFilter instance
*/
constructor(tf) {
super(tf, ClearButton);
super(tf, 'btnReset');
let f = this.config.btn_reset || {};
@ -137,6 +137,3 @@ export class ClearButton extends Feature {
this.initialized = false;
}
}
// TODO: remove as soon as feature name is fixed
ClearButton.meta = {altName: 'btnReset'};

View file

@ -17,7 +17,7 @@ export class DateType extends Feature {
* @param {TableFilter} tf TableFilter instance
*/
constructor(tf) {
super(tf, DateType);
super(tf, 'dateType');
/**
* Global locale

View file

@ -19,7 +19,7 @@ export class Dropdown extends BaseDropdown {
* @param {TableFilter} tf TableFilter instance
*/
constructor(tf) {
super(tf, Dropdown);
super(tf, 'dropdown');
// Configuration object
let f = this.config;

View file

@ -17,7 +17,7 @@ export class GridLayout extends Feature {
* @param {TableFilter} tf TableFilter instance
*/
constructor(tf) {
super(tf, GridLayout);
super(tf, 'gridLayout');
let f = this.config.grid_layout || {};

View file

@ -86,7 +86,7 @@ export class Hash {
}
/**
* Converts a URL hash into a JSON object
* Converts a URL hash into a state JSON object
*
* @param {String} hash URL hash fragment
* @returns {Object} JSON object

View file

@ -4,7 +4,7 @@ import {addEvt, targetEvt, removeEvt} from '../event';
import {NONE} from '../const';
import {root} from '../root';
import {isEmpty, isNull} from '../types';
import {defaultsStr, defaultsNb} from '../settings';
import {defaultsStr} from '../settings';
import {RIGHT} from './toolbar';
const WIKI_URL = 'https://github.com/koalyptus/TableFilter/wiki/' +
@ -21,7 +21,7 @@ export class Help extends Feature {
* @param {TableFilter} tf TableFilter instance
*/
constructor(tf) {
super(tf, Help);
super(tf, 'help');
let f = this.config.help_instructions || {};
@ -92,15 +92,6 @@ export class Help extends Feature {
*/
this.cont = null;
/**
* Adjust container left position when table's horizontal scroll is
* on, typically when `responsive` option is enabled.
* @type {Number}
* @defaultValue 25
*/
this.contAdjustLeftPosition =
defaultsNb(f.container_adjust_left_position, 25);
/**
* Bound mouseup wrapper
* @private
@ -223,21 +214,9 @@ export class Help extends Feature {
let divDisplay = this.cont.style.display;
if (divDisplay === '' || divDisplay === NONE) {
this.cont.style.display = 'inline';
// if table element has an horizontal scrollbar adjust container
// left position accordingly
if (this.tf.dom().scrollLeft > 0) {
this.cont.style.left = `${
this.btn.offsetLeft
- this.tf.dom().scrollLeft
+ this.contAdjustLeftPosition
}px`;
}
addEvt(root, 'mouseup', this.boundMouseup);
} else {
this.cont.style.display = NONE;
this.cont.style.left = '';
}
}
@ -259,6 +238,3 @@ export class Help extends Feature {
}
}
// TODO: remove as soon as feature name is fixed
Help.meta = {alwaysInstantiate: true};

View file

@ -172,9 +172,3 @@ export class HighlightKeyword {
this.highlight(cell, term, this.highlightCssClass);
}
}
// TODO: remove as soon as feature name is fixed
HighlightKeyword.meta = {
name: 'highlightKeyword',
altName: 'highlightKeywords'
};

View file

@ -44,7 +44,7 @@ export class Loader extends Feature {
* @param {TableFilter} tf TableFilter instance
*/
constructor(tf) {
super(tf, Loader);
super(tf, 'loader');
let f = this.config.loader || {};

View file

@ -16,7 +16,7 @@ export class MarkActiveColumns extends Feature {
* @param {TableFilter} tf TableFilter instance
*/
constructor(tf) {
super(tf, MarkActiveColumns);
super(tf, 'markActiveColumns');
let config = this.config.mark_active_columns || {};

View file

@ -17,7 +17,7 @@ export class NoResults extends Feature {
* @param {TableFilter} tf TableFilter instance
*/
constructor(tf) {
super(tf, NoResults);
super(tf, 'noResults');
//configuration object
let f = this.config.no_results_message || {};

View file

@ -21,7 +21,7 @@ export class Paging extends Feature {
* @param {TableFilter} tf TableFilter instance
*/
constructor(tf) {
super(tf, Paging);
super(tf, 'paging');
// Configuration object
let f = this.config.paging || {};

View file

@ -19,7 +19,7 @@ export class PopupFilter extends Feature {
* @param {TableFilter} tf TableFilter instance
*/
constructor(tf) {
super(tf, PopupFilter);
super(tf, 'popupFilters');
// Configuration object
let f = this.config.popup_filters || {};
@ -456,6 +456,3 @@ export class PopupFilter extends Feature {
}
}
// TODO: remove as soon as feature name is fixed
PopupFilter.meta = {altName: 'popupFilters'};

View file

@ -17,7 +17,7 @@ export class RowsCounter extends Feature {
* @param {TableFilter} tf TableFilter instance
*/
constructor(tf) {
super(tf, RowsCounter);
super(tf, 'rowsCounter');
// TableFilter configuration
let f = this.config.rows_counter || {};

View file

@ -19,7 +19,7 @@ export class State extends Feature {
* @param {TableFilter} tf TableFilter instance
*/
constructor(tf) {
super(tf, State);
super(tf, 'state');
let cfg = this.config.state || {};

View file

@ -30,7 +30,7 @@ export class StatusBar extends Feature {
* @param {TableFilter} tf TableFilter instance
*/
constructor(tf) {
super(tf, StatusBar);
super(tf, 'statusBar');
// Configuration object
let f = this.config.status_bar || {};

View file

@ -29,7 +29,7 @@ export class Toolbar extends Feature {
* @memberof Toolbar
*/
constructor(tf) {
super(tf, Toolbar);
super(tf, 'toolbar');
// Configuration object
let f = this.config.toolbar || {};
@ -219,6 +219,3 @@ export class Toolbar extends Feature {
this.initialized = false;
}
}
// TODO: remove as soon as feature name is fixed
Toolbar.meta = {alwaysInstantiate: true};

View file

@ -74,39 +74,3 @@ export const contains = (term, data, exactMatch = false, caseSensitive = false,
}
return regexp.test(data);
};
/**
* Camelize a string, cutting the string by multiple separators like
* hyphens, underscores and spaces.
* @param {String} text text to camelize
* @return {String} camelized text
*/
export const toCamelCase = (text = '') => {
return text.replace(/^([A-Z])|[\s-_]+(\w)/g, (match, p1, p2) => {
if (p2) {
return p2.toUpperCase();
}
return p1.toLowerCase();
});
};
/**
* Generate a string in the format of a UUID (Universally Unique IDentifier).
* NOTE: This format of 8 chars, followed by 3 groups of 4 chars, followed by 12
* chars is known as a UUID and is defined in RFC4122 and is a standard for
* generating unique IDs. This function DOES NOT implement this standard.
* It simply outputs a string that looks similar. The standard is found here:
* https://www.ietf.org/rfc/rfc4122.txt
* source: https://gist.github.com/gordonbrander/2230317
* @return {String}
*/
export const uuid = () => {
const chr4 = () => Math.random().toString(16).slice(-4);
return chr4() + chr4()
+ '-' + chr4()
+ '-' + chr4()
+ '-' + chr4()
+ '-' + chr4()
+ chr4() + chr4();
};

View file

@ -2,7 +2,7 @@ import {addEvt, cancelEvt, stopEvt, targetEvt, isKeyPressed} from './event';
import {
addClass, createElm, elm, getText, getFirstTextNode, removeClass, tag
} from './dom';
import {contains, matchCase, rgxEsc, trim, toCamelCase, uuid} from './string';
import {contains, matchCase, rgxEsc, trim} from './string';
import {
isArray, isEmpty, isFn, isNumber, isObj, isString, isUndef, EMPTY_FN,
isBoolean
@ -17,36 +17,16 @@ import {root} from './root';
import {Emitter} from './emitter';
import {Dropdown} from './modules/dropdown';
import {CheckList} from './modules/checkList';
import {DateType} from './modules/dateType';
import {Help} from './modules/help';
import {State} from './modules/state';
import {GridLayout} from './modules/gridLayout';
import {Loader} from './modules/loader';
import {HighlightKeyword} from './modules/highlightKeywords';
import {PopupFilter} from './modules/popupFilter';
import {MarkActiveColumns} from './modules/markActiveColumns';
import {RowsCounter} from './modules/rowsCounter';
import {StatusBar} from './modules/statusBar';
import {ClearButton} from './modules/clearButton';
import {AlternateRows} from './modules/alternateRows';
import {NoResults} from './modules/noResults';
import {Paging} from './modules/paging';
import {Toolbar} from './modules/toolbar';
import {
INPUT, SELECT, MULTIPLE, CHECKLIST, NONE,
ENTER_KEY, TAB_KEY, ESC_KEY, UP_ARROW_KEY, DOWN_ARROW_KEY,
CELL_TAG, AUTO_FILTER_DELAY, NUMBER, DATE, FORMATTED_NUMBER
CELL_TAG, AUTO_FILTER_DELAY, NUMBER, DATE, FORMATTED_NUMBER,
FEATURES
} from './const';
let doc = root.document;
const FEATURES = [
DateType, Help, State, MarkActiveColumns, GridLayout, Loader,
HighlightKeyword, PopupFilter, RowsCounter, StatusBar, ClearButton,
AlternateRows, NoResults, Paging, Toolbar
];
/**
* Makes HTML tables filterable and a bit more :)
*
@ -142,7 +122,7 @@ export class TableFilter {
args.forEach((arg) => {
if (typeof arg === 'object' && arg.nodeName === 'TABLE') {
this.tbl = arg;
this.id = arg.id || `tf_${uuid()}`;
this.id = arg.id || `tf_${new Date().getTime()}_`;
this.tbl.id = this.id;
} else if (isString(arg)) {
this.id = arg;
@ -337,7 +317,7 @@ export class TableFilter {
this.onAfterFilter = defaultsFn(f.on_after_filter, EMPTY_FN);
/**
* Enable/disable case sensitivity for filtering, default false
* Enable/disable case sensitivity filtering
* @type {Boolean}
*/
this.caseSensitive = Boolean(f.case_sensitive);
@ -419,7 +399,8 @@ export class TableFilter {
* Enable/disable single filter mode
* @type {Boolean|Object}
*/
this.singleFlt = isObj(f.single_filter) || Boolean(f.single_filter);
this.singleFlt = isObj(f.single_filter) ||
Boolean(f.single_filter);
/**
* Specify columns to be excluded from single filter search, by default
@ -493,9 +474,7 @@ export class TableFilter {
* Text for clear option in drop-down filter types (1st option)
* @type {String|Array}
*/
this.clearFilterText = isArray(f.clear_filter_text)
? f.clear_filter_text
: defaultsStr(f.clear_filter_text, 'Clear');
this.clearFilterText = defaultsStr(f.clear_filter_text, 'Clear');
/**
* Indicate whether empty option is enabled in drop-down filter types
@ -534,24 +513,38 @@ export class TableFilter {
* by default globally or on a column basis
* @type {Boolean|Array}
*/
this.sortSlc = isUndef(f.sort_select)
? true
: defaultsArr(f.sort_select, Boolean(f.sort_select));
this.sortSlc = isUndef(f.sort_select) ? true :
isArray(f.sort_select) ? f.sort_select : Boolean(f.sort_select);
/**
* List of columns implementing filter options sorting in ascending
* manner based on column data type
* @type {Array}
* Indicate whether options in drop-down filter types are sorted in a
* ascending numeric manner
* @type {Boolean}
* @private
*/
this.sortFilterOptionsAsc = defaultsArr(f.sort_filter_options_asc, []);
this.isSortNumAsc = Boolean(f.sort_num_asc);
/**
* List of columns implementing filter options sorting in descending
* manner based on column data type
* List of columns implementing options sorting in a ascending numeric
* manner
* @type {Array}
*/
this.sortFilterOptionsDesc =
defaultsArr(f.sort_filter_options_desc, []);
this.sortNumAsc = this.isSortNumAsc ? f.sort_num_asc : [];
/**
* Indicate whether options in drop-down filter types are sorted in a
* descending numeric manner
* @type {Boolean}
* @private
*/
this.isSortNumDesc = Boolean(f.sort_num_desc);
/**
* List of columns implementing options sorting in a descending numeric
* manner
* @type {Array}
*/
this.sortNumDesc = this.isSortNumDesc ? f.sort_num_desc : [];
/**
* Indicate whether drop-down filter types are populated on demand at
@ -933,8 +926,10 @@ export class TableFilter {
*/
this.ExtRegistry = {};
// instantiate features if needed
this.instantiateFeatures(FEATURES);
// conditionally instantiate required features
this.instantiateFeatures(
Object.keys(FEATURES).map((item) => FEATURES[item])
);
}
/**
@ -954,16 +949,20 @@ export class TableFilter {
//loads theme
this.loadThemes();
const { dateType, help, state, markActiveColumns, gridLayout, loader,
highlightKeyword, popupFilter, rowsCounter, statusBar, clearButton,
alternateRows, noResults, paging, toolbar } = FEATURES;
//explicitly initialise features in given order
this.initFeatures([
DateType,
Help,
State,
MarkActiveColumns,
GridLayout,
Loader,
HighlightKeyword,
PopupFilter
dateType,
help,
state,
markActiveColumns,
gridLayout,
loader,
highlightKeyword,
popupFilter
]);
//filters grid is not generated
@ -1036,13 +1035,13 @@ export class TableFilter {
}
this.initFeatures([
RowsCounter,
StatusBar,
ClearButton,
AlternateRows,
NoResults,
Paging,
Toolbar
rowsCounter,
statusBar,
clearButton,
alternateRows,
noResults,
paging,
toolbar
]);
this.setColWidths();
@ -1251,41 +1250,47 @@ export class TableFilter {
}
/**
* Conditionally istantiate each feature class in passed collection if
* required by configuration and add it to the features registry. A feature
* class meta information contains a `name` field and optional `altName` and
* `alwaysInstantiate` fields
* Istantiate the collection of features required by the
* configuration and add them to the features registry. A feature is
* described by a `class` and `name` fields and and optional `property`
* field:
* {
* class: AClass,
* name: 'aClass'
* }
* @param {Array} [features=[]]
* @private
*/
instantiateFeatures(features = []) {
features.forEach(featureCls => {
let Cls = featureCls;
features.forEach((feature) => {
// TODO: remove the property field.
// Due to naming convention inconsistencies, a `property`
// field is added to allow a conditional instanciation based
// on that property on TableFilter, if supplied.
feature.property = feature.property || feature.name;
if (!this.hasConfig || this[feature.property] === true ||
feature.enforce === true) {
let {class: Cls, name} = feature;
// assign meta info if not present
Cls.meta = Cls.meta || {name: null, altName: null};
Cls.meta.name = toCamelCase(Cls.name);
let {name, altName, alwaysInstantiate} = Cls.meta;
let prop = altName || name;
if (!this.hasConfig || this[prop] === true
|| Boolean(alwaysInstantiate)) {
this.Mod[name] = this.Mod[name] || new Cls(this);
}
});
}
/**
* Initialise each feature class in passed collection.
* Initialise the passed features collection. A feature is described by a
* `class` and `name` fields and and optional `property` field:
* {
* class: AClass,
* name: 'aClass'
* }
* @param {Array} [features=[]]
* @private
*/
initFeatures(features = []) {
features.forEach(featureCls => {
let {name, altName} = featureCls.meta;
let prop = altName || name;
if (this[prop] === true && this.Mod[name]) {
features.forEach((feature) => {
let {property, name} = feature;
if (this[property] === true && this.Mod[name]) {
this.Mod[name].init();
}
});
@ -1785,11 +1790,11 @@ export class TableFilter {
}
//empty
else if (hasEM) {
occurence = !cell.hasChildNodes() || isEmpty(cellValue);
occurence = !cell.hasChildNodes();
}
//non-empty
else if (hasNM) {
occurence = cell.hasChildNodes() && !isEmpty(cellValue);
occurence = cell.hasChildNodes();
} else {
occurence = contains(term, cellValue,
this.isExactMatch(colIdx), this.caseSensitive);
@ -1875,11 +1880,11 @@ export class TableFilter {
}
//empty
else if (hasEM) {
occurence = !cell.hasChildNodes() || isEmpty(cellValue);
occurence = !cell.hasChildNodes();
}
//non-empty
else if (hasNM) {
occurence = cell.hasChildNodes() && !isEmpty(cellValue);
occurence = cell.hasChildNodes();
} else {
// If numeric type data, perform a strict equality test and
// fallback to unformatted number string comparison

View file

@ -32,7 +32,6 @@ table.TF
&.resp
display block
overflow-x auto
overflow-y hidden
.sort-arrow
position initial

View file

@ -27,6 +27,7 @@
highlight_column: true
},
no_results_message: true,
responsive: true,
custom_options: {
cols:[3],
texts: [

View file

@ -40,10 +40,6 @@
page_length: true,
sort: true
},
responsive: true,
help_instructions: {
container_adjust_left_position: 30
},
alternate_rows: true,
btn_reset: true,
rows_counter: true,

View file

@ -100,43 +100,19 @@ test('Can sort options', function() {
tf.destroy();
tf = new TableFilter('demo', {
base_path: '../dist/tablefilter/',
col_0: 'checklist',
col_1: 'checklist',
col_2: 'checklist',
col_3: 'checklist',
col_4: 'checklist',
col_types: ['string', 'string', 'number', 'number', 'number'],
sort_filter_options_asc: [0, 2, 3],
sort_filter_options_desc: [1, 4]
sort_num_asc: [2, 3],
sort_num_desc: [4]
});
tf.init();
var flt0 = id(tf.fltIds[0]);
var flt1 = id(tf.fltIds[1]);
var flt2 = id(tf.fltIds[2]);
var flt3 = id(tf.fltIds[3]);
var flt4 = id(tf.fltIds[4]);
deepEqual(
flt0.getElementsByTagName('li')[1].firstChild.firstChild.value,
'Adelaide',
'First option value for column 0'
);
deepEqual(
flt0.getElementsByTagName('li')[2].firstChild.firstChild.value,
'Sydney',
'Last option value for column 0'
);
deepEqual(
flt1.getElementsByTagName('li')[1].firstChild.firstChild.value,
'Perth',
'First option value for column 1'
);
deepEqual(
flt1.getElementsByTagName('li')[6].firstChild.firstChild.value,
'Adelaide',
'Last option value for column 1'
);
deepEqual(
flt2.getElementsByTagName('li')[1].firstChild.firstChild.value,
'286',
@ -206,55 +182,6 @@ test('Can select empty and non-empty options', function() {
'Filter 3 options values attribute');
});
// issue 714, clear filter text
module('Clear filter text');
test('Can define clear filter text for each column', function() {
tf.clearFilters();
tf.destroy();
tf = new TableFilter('demo', {
base_path: '../dist/tablefilter/',
col_2: 'checklist',
col_3: 'checklist',
col_4: 'checklist',
clear_filter_text: [null, null, 'clear 2', 'clear 3', 'clear 4']
});
tf.init();
var flt2 = tf.getFilterElement(2).getElementsByTagName('li');
var flt3 = tf.getFilterElement(3).getElementsByTagName('li');
var flt4 = tf.getFilterElement(4).getElementsByTagName('li');
deepEqual(flt2[0].getElementsByTagName('label')[0].innerText,
'clear 2', 'clear text filter 2');
deepEqual(flt3[0].getElementsByTagName('label')[0].innerText,
'clear 3', 'clear text filter 3');
deepEqual(flt4[0].getElementsByTagName('label')[0].innerText,
'clear 4', 'clear text filter 4');
});
test('Can define clear filter text globally', function() {
tf.clearFilters();
tf.destroy();
tf = new TableFilter('demo', {
base_path: '../dist/tablefilter/',
col_2: 'checklist',
col_3: 'checklist',
col_4: 'checklist',
clear_filter_text: 'reset'
});
tf.init();
var flt2 = tf.getFilterElement(2).getElementsByTagName('li');
var flt3 = tf.getFilterElement(3).getElementsByTagName('li');
var flt4 = tf.getFilterElement(4).getElementsByTagName('li');
deepEqual(flt2[0].getElementsByTagName('label')[0].innerText,
'reset', 'clear text filter 2');
deepEqual(flt3[0].getElementsByTagName('label')[0].innerText,
'reset', 'clear text filter 3');
deepEqual(flt4[0].getElementsByTagName('label')[0].innerText,
'reset', 'clear text filter 4');
});
module('Tear down');
test('TableFilter removed', function() {
tf.destroy();

View file

@ -6,7 +6,6 @@ var tf = new TableFilter('demo', {
tf.init();
var clearButton = tf.feature('clearButton');
module('Sanity checks');
test('Clear button component', function() {
deepEqual(typeof clearButton, 'object', 'ClearButton instanciated');

View file

@ -1,6 +1,4 @@
(function(TableFilter) {
var id = function (id) { return document.getElementById(id); };
(function(win, TableFilter){
// TODO: add sort to test it with different column types
var tf = new TableFilter('demo', {
base_path: '../dist/tablefilter/',
@ -19,6 +17,7 @@
]
});
tf.init();
window.tf = tf;
module('Sanity checks');
test('Data types', function() {
@ -280,7 +279,7 @@
tf.filter();
// assert
deepEqual(tf.getValidRows().length, 7, 'Expected rows');
deepEqual(tf.getValidRows().length, 8, 'Expected rows');
});
module('Locale helpers');
@ -301,92 +300,10 @@
deepEqual(result, '.', 'Decimal separator for given column');
});
module('Data types filters options sorting');
test('Can sort date types', function () {
// setup
tf.clearFilters();
tf.destroy();
tf = new TableFilter('demo', {
base_path: '../dist/tablefilter/',
col_6: 'checklist',
col_7: 'multiple',
col_8: 'checklist',
col_10: 'select',
col_types: [
null, null, null,
{type: 'formatted-number', decimal: ',', thousands: ','},
'formatted-number', null,
{type: 'date', locale: 'fr',},
{type: 'date', locale: 'en', format: '{dd}-{MM}-{yyyy|yy}'},
{
type: 'date', locale: 'en',
format: ['{dd}-{months}-{yyyy|yy}']
},
'IpAddress',
{
type: 'date', locale: 'en',
format: ['{yyyy|yy}-{MM}-{dd} {HH}:{mm}:{ss}']
}
],
sort_filter_options_asc: [6, 7],
sort_filter_options_desc: [8, 10]
});
// act
tf.init();
var flt6 = id(tf.fltIds[6]);
var flt7 = id(tf.fltIds[7]);
var flt8 = id(tf.fltIds[8]);
var flt10 = id(tf.fltIds[10]);
// assert
deepEqual(
flt6.getElementsByTagName('li')[1].firstChild.firstChild.value,
'19/1/1984',
'First option value for column 6'
);
deepEqual(
flt6.getElementsByTagName('li')[20].firstChild.firstChild.value,
'3/7/2002',
'Last option value for column 6'
);
deepEqual(
flt7.options[1].value,
'1/19/1984',
'First option value for column 7'
);
deepEqual(
flt7.options[20].value,
'7/3/2002',
'Last option value for column 7'
);
deepEqual(
flt8.getElementsByTagName('li')[1].firstChild.firstChild.value,
'3-Jul-2002',
'First option value for column 8'
);
deepEqual(
flt8.getElementsByTagName('li')[20].firstChild.firstChild.value,
'19-Jan-1984',
'Last option value for column 8'
);
deepEqual(
flt10.options[1].value,
'03-11-21 12:02:04',
'First option value for column 10'
);
deepEqual(
flt10.options[19].value,
'1899-11-27 02:02:04',
'Last option value for column 10'
);
});
module('Tear-down');
test('can destroy TableFilter DOM elements', function() {
tf.destroy();
deepEqual(tf.isInitialized(), false, 'Filters removed');
});
})(TableFilter);
})(window, TableFilter);

View file

@ -157,31 +157,24 @@ test('Can enable options sorting on a column basis', function() {
deepEqual(flt4.options[7].value, '40', 'Last option value for column 4');
});
test('Can sort options in asc and desc manner', function() {
test('Can sort numeric options in asc and desc manner', function() {
tf.clearFilters();
tf.destroy();
tf = new TableFilter('demo', {
base_path: '../dist/tablefilter/',
col_0: 'select',
col_2: 'multiple',
col_3: 'select',
col_4: 'multiple',
col_types: ['string', 'string', 'number', 'number', 'number'],
case_sensitive: true,
sort_filter_options_asc: [2, 3],
sort_filter_options_desc: [0, 4]
sort_num_asc: [2, 3],
sort_num_desc: [4]
});
tf.init();
var flt0 = id(tf.fltIds[0]);
var flt2 = id(tf.fltIds[2]);
var flt3 = id(tf.fltIds[3]);
var flt4 = id(tf.fltIds[4]);
deepEqual(flt0.options[1].value, 'Sydney',
'First option value for column 0');
deepEqual(flt0.options[2].value, 'Adelaide',
'Last option value for column 0');
deepEqual(flt2.options[1].value, '286', 'First option value for column 2');
deepEqual(flt2.options[7].value, '2781', 'Last option value for column 2');
deepEqual(flt3.options[1].value, '.6', 'First option value for column 3');
@ -190,51 +183,6 @@ test('Can sort options in asc and desc manner', function() {
deepEqual(flt4.options[7].value, '4.3', 'Last option value for column 4');
});
// issue 714, clear filter text
module('Clear filter text');
test('Can define clear filter text for each column', function() {
tf.clearFilters();
tf.destroy();
tf = new TableFilter('demo', {
base_path: '../dist/tablefilter/',
col_2: 'multiple',
col_3: 'select',
col_4: 'multiple',
clear_filter_text: [null, null, 'clear 2', 'clear 3', 'clear 4']
});
tf.init();
var flt2 = tf.getFilterElement(2);
var flt3 = tf.getFilterElement(3);
var flt4 = tf.getFilterElement(4);
deepEqual(flt2.options[0].innerHTML, 'clear 2', 'clear text filter 2');
deepEqual(flt3.options[0].innerHTML, 'clear 3', 'clear text filter 3');
deepEqual(flt4.options[0].innerHTML, 'clear 4', 'clear text filter 4');
});
test('Can define clear filter text globally', function() {
tf.clearFilters();
tf.destroy();
tf = new TableFilter('demo', {
base_path: '../dist/tablefilter/',
col_2: 'multiple',
col_3: 'select',
col_4: 'multiple',
clear_filter_text: 'reset'
});
tf.init();
var flt2 = tf.getFilterElement(2);
var flt3 = tf.getFilterElement(3);
var flt4 = tf.getFilterElement(4);
deepEqual(flt2.options[0].innerHTML, 'reset', 'clear text filter 2');
deepEqual(flt3.options[0].innerHTML, 'reset', 'clear text filter 3');
deepEqual(flt4.options[0].innerHTML, 'reset', 'clear text filter 4');
});
module('Tear down');
test('TableFilter removed', function() {
tf.destroy();
deepEqual(id(tf.fltIds[3]), null, 'Filter is removed');

View file

@ -72,14 +72,7 @@
tf.clearFilters();
tf.setFilterValue(4, '[empty]');
tf.filter();
var filteredData = tf.getFilteredData();
deepEqual(tf.getValidRows().length, 1, 'Expected match');
deepEqual(
filteredData[0],
[6, ['Adelaide', 'Perth', '2781', '3.1', '']],
'Expected row data'
);
deepEqual(tf.getValidRows().length, 0, 'No matches expected');
});
test('Non-empty operator - [nonempty]', function() {
@ -103,7 +96,7 @@
tf.setFilterValue(4, '[nonempty]');
tf.filter();
deepEqual(tf.getValidRows().length, 6, 'Expected number of matches');
deepEqual(tf.getValidRows().length, 7, 'Expected number of matches');
});
test('Or operator - ||', function() {

View file

@ -1,79 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>TableFilter help pop-up with responsive behaviour</title>
<link rel="stylesheet" href="libs/qunit/qunit.css">
<script src="libs/qunit/qunit.js"></script>
<script src="libs/polyfill.js"></script>
</head>
<body>
<div style="float: right; width: 80%; border: 1px solid #f4f4f4;"></div>
<table id="demo" cellpadding="0" cellspacing="0">
<tbody>
<tr>
<th>From</th>
<th>Destination</th>
<th>Road Distance (km)</th>
<th>By Air (hrs)</th>
<th>By Rail (hrs)</th>
</tr>
<tr>
<td><strong>Sydney</strong></td>
<td>Adelaide</td>
<td>1412</td>
<td>1.4</td>
<td>25.3</td>
</tr>
<tr>
<td><strong>Sydney</strong></td>
<td>Brisbane</td>
<td>982</td>
<td>1.5</td>
<td>16</td>
</tr>
<tr>
<td><strong>Sydney</strong></td>
<td>Canberra</td>
<td>286</td>
<td>.6</td>
<td>4.3</td>
</tr>
<tr>
<td><strong>Sydney</strong></td>
<td>Melbourne</td>
<td>872</td>
<td>1.1</td>
<td>10.5</td>
</tr>
<tr>
<td><strong>Adelaide</strong></td>
<td>Perth</td>
<td>2781</td>
<td>3.1</td>
<td>38</td>
</tr>
<tr>
<td><strong>Adelaide</strong></td>
<td>Alice Springs</td>
<td>1533</td>
<td>2</td>
<td>20.25</td>
</tr>
<tr>
<td><strong>Adelaide</strong></td>
<td>Brisbane</td>
<td>2045</td>
<td>2.15</td>
<td>40</td>
</tr>
</tbody>
</table>
<script src="../dist/tablefilter/tablefilter.js"></script>
<script src="test-help-responsive.js"></script>
<div id="qunit"></div>
<div id="qunit-fixture"></div>
</body>
</html>

View file

@ -1,70 +0,0 @@
var tf = new TableFilter('demo', {
base_path: '../dist/tablefilter/',
help_instructions: {
container_adjust_left_position: 20
},
responsive: true
});
tf.init();
var help = tf.feature('help');
module('Sanity checks');
test('Button element', function() {
deepEqual(typeof help, 'object', 'Help instanciated');
notEqual(help.btn, null, 'btn property');
});
module('Pop-up container position');
test('Help UI elements', function() {
var container = help.cont,
helpBtn = help.btn;
deepEqual(container.nodeName, 'DIV', 'Help container');
deepEqual(helpBtn.nodeName, 'SPAN', 'Help button');
});
// 772 issue: pop-up container position when table feature horizontal scroll
test('When table has horizontal scroll', function() {
// setup
tf.dom().scrollLeft = 10000;
// act
help.toggle();
// assert
deepEqual(
parseFloat(help.cont.style.left),
(help.btn.offsetLeft
- tf.dom().scrollLeft
+ help.contAdjustLeftPosition),
'Pop-up container position'
);
});
test('When table does not have horizontal scroll', function() {
tf.destroy();
tf = new TableFilter('demo', {
base_path: '../dist/tablefilter/',
help_instructions: true,
responsive: false
});
tf.init();
var help = tf.feature('help');
// act
help.toggle();
// assert
deepEqual(help.cont.style.left, '', 'Pop-up container position');
});
module('Tear-down');
test('can destroy Help UI component', function() {
// act
tf.destroy();
var help = tf.feature('help');
// assert
deepEqual(help.btn, null, 'Help button removed');
deepEqual(help.cont, null, 'Help pop-up container removed');
});

View file

@ -1,6 +1,6 @@
const webpack = require('webpack');
const path = require('path');
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
const Clean = require('clean-webpack-plugin');
const StringReplacePlugin = require('string-replace-webpack-plugin');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const fs = require('fs');
@ -52,14 +52,14 @@ module.exports = {
}
]
},
// devtool: 'source-map',
devtool: 'source-map',
optimization: {
minimizer: [
new UglifyJsPlugin({
// sourceMap: true,
sourceMap: true,
uglifyOptions: {
beautify: false,
warnings: false,
compress: {warnings: false},
comments: false,
keep_fnames: true
}
@ -67,9 +67,7 @@ module.exports = {
]
},
plugins: [
new CleanWebpackPlugin({
cleanOnceBeforeBuildPatterns: [path.join(process.cwd(), 'dist')]
}),
new Clean(['dist']),
new webpack.optimize.AggressiveMergingPlugin(),
new StringReplacePlugin(),
new webpack.optimize.MinChunkSizePlugin({

View file

@ -1,7 +1,7 @@
const webpack = require('webpack');
const webpackConfig = require('./webpack.config.js');
const path = require('path');
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
const Clean = require('clean-webpack-plugin');
module.exports = {
mode: 'development',
@ -23,9 +23,7 @@ module.exports = {
},
devtool: 'source-map',
plugins: [
new CleanWebpackPlugin({
cleanOnceBeforeBuildPatterns: [path.join(process.cwd(), 'dist')]
}),
new Clean(['dist']),
new webpack.LoaderOptionsPlugin({
debug: true,
options: {