1
0
Fork 0
mirror of https://github.com/koalyptus/TableFilter.git synced 2024-05-15 21:06:44 +02:00
TableFilter/src/tablefilter.js

2302 lines
76 KiB
JavaScript
Raw Normal View History

2016-06-02 06:13:56 +02:00
import {addEvt, cancelEvt, stopEvt, targetEvt, keyCode} from './event';
2016-05-24 10:42:11 +02:00
import {
2016-05-25 09:31:53 +02:00
addClass, createElm, createOpt, elm, getText, getFirstTextNode, hasClass,
removeClass, removeElm, tag
2016-05-24 10:42:11 +02:00
} from './dom';
2016-05-20 10:08:39 +02:00
import {contains, matchCase, rgxEsc, trim} from './string';
import {isEmpty as isEmptyString} from './string';
2016-05-15 04:56:12 +02:00
import {isArray, isEmpty, isFn, isNumber, isObj, isString, isUndef}
2016-05-19 04:50:18 +02:00
from './types';
2016-05-21 03:33:49 +02:00
import {formatDate, isValidDate} from './date';
2016-05-20 09:21:42 +02:00
import {removeNbFormat} from './helpers';
2015-02-22 12:46:05 +01:00
2016-05-08 07:26:52 +02:00
import {root} from './root';
import {Emitter} from './emitter';
import {GridLayout} from './modules/gridLayout';
import {Loader} from './modules/loader';
import {HighlightKeyword} from './modules/highlightKeywords';
import {PopupFilter} from './modules/popupFilter';
import {Dropdown} from './modules/dropdown';
import {CheckList} from './modules/checkList';
import {RowsCounter} from './modules/rowsCounter';
import {StatusBar} from './modules/statusBar';
import {Paging} from './modules/paging';
import {ClearButton} from './modules/clearButton';
import {Help} from './modules/help';
import {AlternateRows} from './modules/alternateRows';
2015-12-08 12:53:12 +01:00
import {NoResults} from './modules/noResults';
2016-03-19 15:10:59 +01:00
import {State} from './modules/state';
2015-02-28 10:27:28 +01:00
2016-05-08 03:48:51 +02:00
import {
INPUT, SELECT, MULTIPLE, CHECKLIST, NONE,
ENTER_KEY, TAB_KEY, ESC_KEY, UP_ARROW_KEY, DOWN_ARROW_KEY,
CELL_TAG, AUTO_FILTER_DELAY
} from './const';
2016-05-08 07:26:52 +02:00
let doc = root.document;
2015-02-22 12:46:05 +01:00
export class TableFilter {
2015-02-22 12:46:05 +01:00
/**
* TableFilter object constructor
* requires `table` or `id` arguments, `row` and `configuration` optional
* @param {DOMElement} table Table DOM element
2015-02-22 12:46:05 +01:00
* @param {String} id Table id
* @param {Number} row index indicating the 1st row
* @param {Object} configuration object
*/
constructor(...args) {
this.id = null;
this.version = '{VERSION}';
2015-02-22 12:46:05 +01:00
this.year = new Date().getFullYear();
this.tbl = null;
2015-02-22 12:46:05 +01:00
this.startRow = null;
this.refRow = null;
this.headersRow = null;
this.cfg = {};
this.nbFilterableRows = 0;
2015-02-22 12:46:05 +01:00
this.nbCells = null;
2016-05-15 04:56:12 +02:00
// TODO: use for-of
2016-04-30 04:58:15 +02:00
args.forEach((arg) => {
2016-05-15 04:56:12 +02:00
if (typeof arg === 'object' && arg.nodeName === 'TABLE') {
this.tbl = arg;
this.id = arg.id || `tf_${new Date().getTime()}_`;
2016-05-15 04:56:12 +02:00
} else if (isString(arg)) {
this.id = arg;
2016-05-25 09:31:53 +02:00
this.tbl = elm(arg);
2016-05-15 04:56:12 +02:00
} else if (isNumber(arg)) {
this.startRow = arg;
2016-05-15 04:56:12 +02:00
} else if (isObj(arg)) {
this.cfg = arg;
}
});
2015-02-22 12:46:05 +01:00
if (!this.tbl || this.tbl.nodeName !== 'TABLE' ||
2016-04-30 04:58:15 +02:00
this.getRowsNb() === 0) {
2016-05-15 04:56:12 +02:00
throw new Error(`Could not instantiate TableFilter: HTML table
DOM element not found.`);
2015-02-22 12:46:05 +01:00
}
// configuration object
2015-05-30 14:23:33 +02:00
let f = this.cfg;
2015-02-22 12:46:05 +01:00
/**
* Event emitter instance
* @type {Emitter}
*/
this.emitter = new Emitter();
2015-02-22 12:46:05 +01:00
//Start row et cols nb
2016-04-30 04:58:15 +02:00
this.refRow = this.startRow === null ? 2 : (this.startRow + 1);
try { this.nbCells = this.getCellsNb(this.refRow); }
catch (e) { this.nbCells = this.getCellsNb(0); }
2015-02-22 12:46:05 +01:00
//default script base path
this.basePath = f.base_path || 'tablefilter/';
2015-02-22 12:46:05 +01:00
/*** filters' grid properties ***/
//enables/disables filter grid
this.fltGrid = f.grid === false ? false : true;
2015-02-22 12:46:05 +01:00
//enables/disables grid layout (fixed headers)
2015-06-04 15:04:38 +02:00
this.gridLayout = Boolean(f.grid_layout);
2015-02-22 12:46:05 +01:00
2015-08-11 13:28:26 +02:00
this.filtersRowIndex = isNaN(f.filters_row_index) ?
0 : f.filters_row_index;
this.headersRow = isNaN(f.headers_row_index) ?
(this.filtersRowIndex === 0 ? 1 : 0) : f.headers_row_index;
2015-02-22 12:46:05 +01:00
//defines tag of the cells containing filters (td/th)
2016-05-15 04:56:12 +02:00
this.fltCellTag = isString(f.filters_cell_tag) ?
2016-05-08 03:48:51 +02:00
f.filters_cell_tag : CELL_TAG;
2015-02-22 12:46:05 +01:00
//stores filters ids
this.fltIds = [];
//stores valid rows indexes (rows visible upon filtering)
2015-12-31 02:56:50 +01:00
this.validRowsIndex = [];
2015-02-22 12:46:05 +01:00
//container div for paging elements, reset btn etc.
this.infDiv = null;
//div for rows counter
this.lDiv = null;
//div for reset button and results per page select
this.rDiv = null;
//div for paging elements
this.mDiv = null;
2015-05-15 16:26:21 +02:00
//defines css class for div containing paging elements, rows counter etc
2015-02-22 12:46:05 +01:00
this.infDivCssClass = f.inf_div_css_class || 'inf';
//defines css class for left div
this.lDivCssClass = f.left_div_css_class || 'ldiv';
//defines css class for right div
2016-02-16 08:41:47 +01:00
this.rDivCssClass = f.right_div_css_class || 'rdiv';
2015-02-22 12:46:05 +01:00
//defines css class for mid div
this.mDivCssClass = f.middle_div_css_class || 'mdiv';
//table container div css class
this.contDivCssClass = f.content_div_css_class || 'cont';
/*** filters' grid appearance ***/
//stylesheet file
2015-06-06 14:22:13 +02:00
this.stylePath = f.style_path || this.basePath + 'style/';
2016-04-30 04:58:15 +02:00
this.stylesheet = f.stylesheet || this.stylePath + 'tablefilter.css';
2015-02-22 12:46:05 +01:00
this.stylesheetId = this.id + '_style';
//defines css class for filters row
this.fltsRowCssClass = f.flts_row_css_class || 'fltrow';
2016-04-30 04:58:15 +02:00
//enables/disables icons (paging, reset button)
this.enableIcons = f.enable_icons === false ? false : true;
2015-02-22 12:46:05 +01:00
//enables/disbles rows alternating bg colors
2015-11-14 16:14:13 +01:00
this.alternateRows = Boolean(f.alternate_rows);
2015-02-22 12:46:05 +01:00
//defines widths of columns
2016-05-15 04:56:12 +02:00
this.hasColWidths = isArray(f.col_widths);
2016-06-13 04:49:27 +02:00
this.colWidths = this.hasColWidths ? f.col_widths : [];
2015-02-22 12:46:05 +01:00
//defines css class for filters
this.fltCssClass = f.flt_css_class || 'flt';
//defines css class for multiple selects filters
this.fltMultiCssClass = f.flt_multi_css_class || 'flt_multi';
//defines css class for filters
this.fltSmallCssClass = f.flt_small_css_class || 'flt_s';
//defines css class for single-filter
this.singleFltCssClass = f.single_flt_css_class || 'single_flt';
/*** filters' grid behaviours ***/
//enables/disables enter key
2016-04-30 04:58:15 +02:00
this.enterKey = f.enter_key === false ? false : true;
2015-02-22 12:46:05 +01:00
//calls function before filtering starts
2016-05-15 04:56:12 +02:00
this.onBeforeFilter = isFn(f.on_before_filter) ?
2015-02-22 12:46:05 +01:00
f.on_before_filter : null;
//calls function after filtering
2016-05-15 04:56:12 +02:00
this.onAfterFilter = isFn(f.on_after_filter) ? f.on_after_filter : null;
2015-02-22 12:46:05 +01:00
//enables/disables case sensitivity
2015-06-04 15:04:38 +02:00
this.caseSensitive = Boolean(f.case_sensitive);
//has exact match per column
2016-05-15 04:56:12 +02:00
this.hasExactMatchByCol = isArray(f.columns_exact_match);
this.exactMatchByCol = this.hasExactMatchByCol ?
f.columns_exact_match : [];
2015-02-22 12:46:05 +01:00
//enables/disbles exact match for search
2015-06-04 15:04:38 +02:00
this.exactMatch = Boolean(f.exact_match);
2015-02-22 12:46:05 +01:00
//refreshes drop-down lists upon validation
2015-06-04 15:04:38 +02:00
this.linkedFilters = Boolean(f.linked_filters);
2015-02-22 12:46:05 +01:00
//wheter excluded options are disabled
2015-06-04 15:04:38 +02:00
this.disableExcludedOptions = Boolean(f.disable_excluded_options);
2015-02-22 12:46:05 +01:00
//id of active filter
this.activeFilterId = null;
//enables always visible rows
2015-06-04 15:04:38 +02:00
this.hasVisibleRows = Boolean(f.rows_always_visible);
2015-02-22 12:46:05 +01:00
//array containing always visible rows
this.visibleRows = this.hasVisibleRows ? f.rows_always_visible : [];
//enables/disables external filters generation
2015-06-04 15:04:38 +02:00
this.isExternalFlt = Boolean(f.external_flt_grid);
2015-02-22 12:46:05 +01:00
//array containing ids of external elements containing filters
this.externalFltTgtIds = f.external_flt_grid_ids || [];
2015-02-22 12:46:05 +01:00
//stores filters elements if isExternalFlt is true
this.externalFltEls = [];
//calls function when filters grid loaded
2016-05-15 04:56:12 +02:00
this.onFiltersLoaded = isFn(f.on_filters_loaded) ?
2015-02-22 12:46:05 +01:00
f.on_filters_loaded : null;
//enables/disables single filter search
2015-08-11 13:28:26 +02:00
this.singleSearchFlt = Boolean(f.single_filter);
2015-02-22 12:46:05 +01:00
//calls function after row is validated
2016-05-15 04:56:12 +02:00
this.onRowValidated = isFn(f.on_row_validated) ?
2015-02-22 12:46:05 +01:00
f.on_row_validated : null;
//array defining columns for customCellData event
this.customCellDataCols = f.custom_cell_data_cols ?
f.custom_cell_data_cols : [];
//calls custom function for retrieving cell data
2016-05-15 04:56:12 +02:00
this.customCellData = isFn(f.custom_cell_data) ?
2015-02-22 12:46:05 +01:00
f.custom_cell_data : null;
//input watermark text array
this.watermark = f.watermark || '';
2016-05-15 04:56:12 +02:00
this.isWatermarkArray = isArray(this.watermark);
2015-02-22 12:46:05 +01:00
//id of toolbar container element
this.toolBarTgtId = f.toolbar_target_id || null;
//enables/disables help div
2016-05-15 04:56:12 +02:00
this.help = isUndef(f.help_instructions) ?
2015-06-05 15:10:25 +02:00
undefined : Boolean(f.help_instructions);
2015-02-22 12:46:05 +01:00
//popup filters
this.popupFilters = Boolean(f.popup_filters);
2015-02-22 12:46:05 +01:00
//active columns color
2015-06-04 15:04:38 +02:00
this.markActiveColumns = Boolean(f.mark_active_columns);
2015-02-22 12:46:05 +01:00
//defines css class for active column header
this.activeColumnsCssClass = f.active_columns_css_class ||
'activeHeader';
//calls function before active column header is marked
2016-05-15 04:56:12 +02:00
this.onBeforeActiveColumn = isFn(f.on_before_active_column) ?
2015-02-22 12:46:05 +01:00
f.on_before_active_column : null;
//calls function after active column header is marked
2016-05-15 04:56:12 +02:00
this.onAfterActiveColumn = isFn(f.on_after_active_column) ?
2015-02-22 12:46:05 +01:00
f.on_after_active_column : null;
/*** select filter's customisation and behaviours ***/
//defines 1st option text
2015-06-09 10:46:08 +02:00
this.displayAllText = f.display_all_text || 'Clear';
2015-02-22 12:46:05 +01:00
//enables/disables empty option in combo-box filters
2015-06-04 15:04:38 +02:00
this.enableEmptyOption = Boolean(f.enable_empty_option);
2015-02-22 12:46:05 +01:00
//defines empty option text
this.emptyText = f.empty_text || '(Empty)';
//enables/disables non empty option in combo-box filters
2015-06-04 15:04:38 +02:00
this.enableNonEmptyOption = Boolean(f.enable_non_empty_option);
2015-02-22 12:46:05 +01:00
//defines empty option text
this.nonEmptyText = f.non_empty_text || '(Non empty)';
//enables/disables onChange event on combo-box
2016-04-30 04:58:15 +02:00
this.onSlcChange = f.on_change === false ? false : true;
2015-02-22 12:46:05 +01:00
//enables/disables select options sorting
2016-04-30 04:58:15 +02:00
this.sortSlc = f.sort_select === false ? false : true;
2015-02-22 12:46:05 +01:00
//enables/disables ascending numeric options sorting
2015-06-04 15:04:38 +02:00
this.isSortNumAsc = Boolean(f.sort_num_asc);
this.sortNumAsc = this.isSortNumAsc ? f.sort_num_asc : [];
2015-02-22 12:46:05 +01:00
//enables/disables descending numeric options sorting
2015-06-04 15:04:38 +02:00
this.isSortNumDesc = Boolean(f.sort_num_desc);
this.sortNumDesc = this.isSortNumDesc ? f.sort_num_desc : [];
//Select filters are populated on demand
this.loadFltOnDemand = Boolean(f.load_filters_on_demand);
2016-05-15 04:56:12 +02:00
this.hasCustomOptions = isObj(f.custom_options);
this.customOptions = f.custom_options;
2015-02-22 12:46:05 +01:00
/*** Filter operators ***/
this.rgxOperator = f.regexp_operator || 'rgx:';
this.emOperator = f.empty_operator || '[empty]';
this.nmOperator = f.nonempty_operator || '[nonempty]';
this.orOperator = f.or_operator || '||';
this.anOperator = f.and_operator || '&&';
this.grOperator = f.greater_operator || '>';
this.lwOperator = f.lower_operator || '<';
this.leOperator = f.lower_equal_operator || '<=';
this.geOperator = f.greater_equal_operator || '>=';
this.dfOperator = f.different_operator || '!';
this.lkOperator = f.like_operator || '*';
this.eqOperator = f.equal_operator || '=';
this.stOperator = f.start_with_operator || '{';
this.enOperator = f.end_with_operator || '}';
this.curExp = f.cur_exp || '^[¥£€$]';
this.separator = f.separator || ',';
//show/hides rows counter
2015-06-04 15:04:38 +02:00
this.rowsCounter = Boolean(f.rows_counter);
2015-02-22 12:46:05 +01:00
//show/hides status bar
2015-06-04 15:04:38 +02:00
this.statusBar = Boolean(f.status_bar);
2015-02-22 12:46:05 +01:00
//enables/disables loader/spinner indicator
2015-06-04 15:04:38 +02:00
this.loader = Boolean(f.loader);
2015-02-22 12:46:05 +01:00
/*** validation - reset buttons/links ***/
//show/hides filter's validation button
2015-06-04 15:04:38 +02:00
this.displayBtn = Boolean(f.btn);
2015-02-22 12:46:05 +01:00
//defines validation button text
this.btnText = f.btn_text || (!this.enableIcons ? 'Go' : '');
//defines css class for validation button
this.btnCssClass = f.btn_css_class ||
(!this.enableIcons ? 'btnflt' : 'btnflt_icon');
//show/hides reset link
2015-06-04 15:04:38 +02:00
this.btnReset = Boolean(f.btn_reset);
2015-02-22 12:46:05 +01:00
//defines css class for reset button
this.btnResetCssClass = f.btn_reset_css_class || 'reset';
//callback function before filters are cleared
2016-05-15 04:56:12 +02:00
this.onBeforeReset = isFn(f.on_before_reset) ?
2015-02-22 12:46:05 +01:00
f.on_before_reset : null;
//callback function after filters are cleared
2016-05-15 04:56:12 +02:00
this.onAfterReset = isFn(f.on_after_reset) ? f.on_after_reset : null;
2015-02-22 12:46:05 +01:00
/*** paging ***/
//enables/disables table paging
2015-06-04 15:04:38 +02:00
this.paging = Boolean(f.paging);
2015-02-22 12:46:05 +01:00
this.nbHiddenRows = 0; //nb hidden rows
/*** autofilter on typing ***/
2016-04-30 04:58:15 +02:00
//Auto filtering, table is filtered when user stops typing
2015-06-04 15:04:38 +02:00
this.autoFilter = Boolean(f.auto_filter);
2015-02-22 12:46:05 +01:00
//onkeyup delay timer (msecs)
2015-06-04 15:04:38 +02:00
this.autoFilterDelay = !isNaN(f.auto_filter_delay) ?
2016-05-08 03:48:51 +02:00
f.auto_filter_delay : AUTO_FILTER_DELAY;
2015-06-04 15:04:38 +02:00
//typing indicator
this.isUserTyping = null;
this.autoFilterTimer = null;
2015-02-22 12:46:05 +01:00
/*** keyword highlighting ***/
//enables/disables keyword highlighting
2015-06-04 15:04:38 +02:00
this.highlightKeywords = Boolean(f.highlight_keywords);
2015-02-22 12:46:05 +01:00
2015-12-08 12:53:12 +01:00
/*** No results feature ***/
2016-05-15 04:56:12 +02:00
this.noResults = isObj(f.no_results_message) ||
2015-12-08 12:53:12 +01:00
Boolean(f.no_results_message);
2016-05-29 12:44:27 +02:00
// state persisstence
2016-05-15 04:56:12 +02:00
this.state = isObj(f.state) || Boolean(f.state);
2016-03-14 07:49:23 +01:00
2015-02-22 12:46:05 +01:00
/*** data types ***/
//defines default date type (european DMY)
this.defaultDateType = f.default_date_type || 'DMY';
2016-05-07 11:20:42 +02:00
//defines default thousands separator US = ',' EU = '.'
2015-02-22 12:46:05 +01:00
this.thousandsSeparator = f.thousands_separator || ',';
//defines default decimal separator
//US & javascript = '.' EU = ','
this.decimalSeparator = f.decimal_separator || '.';
//enables number format per column
2016-05-15 04:56:12 +02:00
this.hasColNbFormat = isArray(f.col_number_format);
2015-02-22 12:46:05 +01:00
//array containing columns nb formats
2015-06-08 12:21:50 +02:00
this.colNbFormat = this.hasColNbFormat ? f.col_number_format : null;
2015-02-22 12:46:05 +01:00
//enables date type per column
2016-05-15 04:56:12 +02:00
this.hasColDateType = isArray(f.col_date_type);
2015-02-22 12:46:05 +01:00
//array containing columns date type
2015-06-04 15:04:38 +02:00
this.colDateType = this.hasColDateType ? f.col_date_type : null;
2015-02-22 12:46:05 +01:00
/*** ids prefixes ***/
//css class name added to table
this.prfxTf = 'TF';
//filters (inputs - selects)
this.prfxFlt = 'flt';
//validation button
this.prfxValButton = 'btn';
//container div for paging elements, rows counter etc.
this.prfxInfDiv = 'inf_';
//left div
this.prfxLDiv = 'ldiv_';
//right div
this.prfxRDiv = 'rdiv_';
//middle div
this.prfxMDiv = 'mdiv_';
//responsive table css class
this.prfxResponsive = 'resp';
2015-02-22 12:46:05 +01:00
/*** extensions ***/
//imports external script
this.extensions = f.extensions;
2016-05-15 04:56:12 +02:00
this.hasExtensions = isArray(this.extensions);
2015-02-22 12:46:05 +01:00
/*** themes ***/
2015-06-04 15:04:38 +02:00
this.enableDefaultTheme = Boolean(f.enable_default_theme);
2015-02-22 12:46:05 +01:00
//imports themes
2016-05-15 04:56:12 +02:00
this.hasThemes = (this.enableDefaultTheme || isArray(f.themes));
2015-05-15 16:26:21 +02:00
this.themes = f.themes || [];
2015-02-22 12:46:05 +01:00
//themes path
2015-06-06 14:22:13 +02:00
this.themesPath = f.themes_path || this.stylePath + 'themes/';
2015-02-22 12:46:05 +01:00
//responsive table
this.responsive = Boolean(f.responsive);
// Features registry
2015-06-08 12:21:50 +02:00
this.Mod = {};
2015-02-22 12:46:05 +01:00
// Extensions registry
2015-06-05 15:10:25 +02:00
this.ExtRegistry = {};
2015-02-22 12:46:05 +01:00
}
/**
* Initialise features and layout
*/
2016-04-30 04:58:15 +02:00
init() {
2016-05-29 12:44:27 +02:00
if (this.initialized) {
2015-02-22 12:46:05 +01:00
return;
}
let Mod = this.Mod;
2016-05-01 11:57:23 +02:00
let n = this.singleSearchFlt ? 1 : this.nbCells;
let inpclass;
2015-02-22 12:46:05 +01:00
//loads stylesheet if not imported
2015-05-15 16:26:21 +02:00
this.import(this.stylesheetId, this.stylesheet, null, 'link');
2015-02-22 12:46:05 +01:00
//loads theme
2016-04-30 04:58:15 +02:00
if (this.hasThemes) {
this.loadThemes();
}
2015-02-22 12:46:05 +01:00
2016-01-19 13:13:34 +01:00
// Instantiate help feature and initialise only if set true
2016-04-30 04:58:15 +02:00
if (!Mod.help) {
2016-01-19 13:13:34 +01:00
Mod.help = new Help(this);
}
2016-04-30 04:58:15 +02:00
if (this.help) {
2016-01-19 13:13:34 +01:00
Mod.help.init();
}
2016-04-30 04:58:15 +02:00
if (this.state) {
if (!Mod.state) {
2016-03-19 15:10:59 +01:00
Mod.state = new State(this);
2016-03-14 07:49:23 +01:00
}
2016-03-19 15:10:59 +01:00
Mod.state.init();
2016-03-14 07:49:23 +01:00
}
2016-04-30 04:58:15 +02:00
if (this.gridLayout) {
if (!Mod.gridLayout) {
2016-01-13 07:45:44 +01:00
Mod.gridLayout = new GridLayout(this);
}
Mod.gridLayout.init();
2015-02-22 12:46:05 +01:00
}
2016-04-30 04:58:15 +02:00
if (this.loader) {
if (!Mod.loader) {
Mod.loader = new Loader(this);
2015-02-22 12:46:05 +01:00
}
Mod.loader.init();
2015-02-22 12:46:05 +01:00
}
2016-04-30 04:58:15 +02:00
if (this.highlightKeywords) {
Mod.highlightKeyword = new HighlightKeyword(this);
2015-12-29 07:13:08 +01:00
Mod.highlightKeyword.init();
2015-02-22 12:46:05 +01:00
}
2016-04-30 04:58:15 +02:00
if (this.popupFilters) {
if (!Mod.popupFilter) {
Mod.popupFilter = new PopupFilter(this);
2015-02-22 12:46:05 +01:00
}
Mod.popupFilter.init();
2015-02-22 12:46:05 +01:00
}
//filters grid is not generated
2016-04-30 04:58:15 +02:00
if (!this.fltGrid) {
2016-01-04 07:59:30 +01:00
this._initNoFilters();
2015-02-22 12:46:05 +01:00
} else {
let fltrow = this._insertFiltersRow();
2015-02-22 12:46:05 +01:00
this.nbFilterableRows = this.getRowsNb();
2015-02-22 12:46:05 +01:00
// Generate filters
2016-04-30 04:58:15 +02:00
for (let i = 0; i < n; i++) {
this.emitter.emit('before-filter-init', this, i);
2015-02-22 12:46:05 +01:00
2016-05-24 10:42:11 +02:00
let fltcell = createElm(this.fltCellTag),
col = this.getFilterType(i);
2015-02-22 12:46:05 +01:00
2016-04-30 04:58:15 +02:00
if (this.singleSearchFlt) {
fltcell.colSpan = this.nbCells;
}
2016-04-30 04:58:15 +02:00
if (!this.gridLayout) {
fltrow.appendChild(fltcell);
}
2016-05-15 05:33:16 +02:00
inpclass = (i === n - 1 && this.displayBtn) ?
this.fltSmallCssClass : this.fltCssClass;
2015-02-22 12:46:05 +01:00
//only 1 input for single search
2016-04-30 04:58:15 +02:00
if (this.singleSearchFlt) {
col = INPUT;
inpclass = this.singleFltCssClass;
}
2015-02-22 12:46:05 +01:00
//drop-down filters
if (col === SELECT || col === MULTIPLE) {
2016-04-30 04:58:15 +02:00
if (!Mod.dropdown) {
Mod.dropdown = new Dropdown(this);
2015-02-22 12:46:05 +01:00
}
Mod.dropdown.init(i, this.isExternalFlt, fltcell);
}
// checklist
else if (col === CHECKLIST) {
2016-04-30 04:58:15 +02:00
if (!Mod.checkList) {
Mod.checkList = new CheckList(this);
}
Mod.checkList.init(i, this.isExternalFlt, fltcell);
} else {
this._buildInputFilter(i, inpclass, fltcell);
}
2015-02-22 12:46:05 +01:00
// this adds submit button
2016-05-15 05:33:16 +02:00
if (i === n - 1 && this.displayBtn) {
this._buildSubmitButton(i, fltcell);
}
2015-02-22 12:46:05 +01:00
this.emitter.emit('after-filter-init', this, i);
}
2015-02-22 12:46:05 +01:00
2016-04-05 10:51:56 +02:00
this.emitter.on(['filter-focus'],
2016-04-30 04:58:15 +02:00
(tf, filter) => this.setActiveFilterId(filter.id));
2016-04-05 10:51:56 +02:00
2015-02-22 12:46:05 +01:00
}//if this.fltGrid
2015-12-08 12:53:12 +01:00
/* Features */
2016-04-30 04:58:15 +02:00
if (this.hasVisibleRows) {
this.emitter.on(['after-filtering'],
() => this.enforceVisibility());
this.enforceVisibility();
}
2016-04-30 04:58:15 +02:00
if (this.rowsCounter) {
Mod.rowsCounter = new RowsCounter(this);
Mod.rowsCounter.init();
2015-02-22 12:46:05 +01:00
}
2016-04-30 04:58:15 +02:00
if (this.statusBar) {
Mod.statusBar = new StatusBar(this);
Mod.statusBar.init();
2015-02-22 12:46:05 +01:00
}
2016-04-30 04:58:15 +02:00
if (this.paging) {
if (!Mod.paging) {
2015-06-13 11:03:33 +02:00
Mod.paging = new Paging(this);
Mod.paging.init();
2016-04-30 04:58:15 +02:00
} else {
2016-02-22 08:14:58 +01:00
Mod.paging.reset();
2015-06-13 11:03:33 +02:00
}
2015-02-22 12:46:05 +01:00
}
2016-04-30 04:58:15 +02:00
if (this.btnReset) {
Mod.clearButton = new ClearButton(this);
Mod.clearButton.init();
2015-02-22 12:46:05 +01:00
}
2016-01-19 13:13:34 +01:00
2016-04-30 04:58:15 +02:00
if (this.hasColWidths && !this.gridLayout) {
2015-02-22 12:46:05 +01:00
this.setColWidths();
}
2016-04-30 04:58:15 +02:00
if (this.alternateRows) {
Mod.alternateRows = new AlternateRows(this);
Mod.alternateRows.init();
2015-02-22 12:46:05 +01:00
}
2016-04-30 04:58:15 +02:00
if (this.noResults) {
if (!Mod.noResults) {
2015-12-08 12:53:12 +01:00
Mod.noResults = new NoResults(this);
}
Mod.noResults.init();
}
2015-02-22 12:46:05 +01:00
//TF css class is added to table
2016-04-30 04:58:15 +02:00
if (!this.gridLayout) {
2016-05-24 10:42:11 +02:00
addClass(this.tbl, this.prfxTf);
2016-04-30 04:58:15 +02:00
if (this.responsive) {
2016-05-24 10:42:11 +02:00
addClass(this.tbl, this.prfxResponsive);
}
2015-02-22 12:46:05 +01:00
}
/* Loads extensions */
2016-04-30 04:58:15 +02:00
if (this.hasExtensions) {
2015-05-10 13:12:31 +02:00
this.initExtensions();
2015-02-22 12:46:05 +01:00
}
// Subscribe to events
2016-04-30 04:58:15 +02:00
if (this.markActiveColumns) {
2016-01-03 03:49:04 +01:00
this.emitter.on(['before-filtering'],
2016-04-30 04:58:15 +02:00
() => this.clearActiveColumns());
2016-01-03 03:49:04 +01:00
this.emitter.on(['cell-processed'],
2016-04-30 04:58:15 +02:00
(tf, colIndex) => this.markActiveColumn(colIndex));
}
2016-04-30 04:58:15 +02:00
if (this.linkedFilters) {
this.emitter.on(['after-filtering'], () => this.linkFilters());
}
this.initialized = true;
2016-04-30 04:58:15 +02:00
if (this.onFiltersLoaded) {
2015-02-22 12:46:05 +01:00
this.onFiltersLoaded.call(null, this);
}
this.emitter.emit('initialized', this);
2015-02-22 12:46:05 +01:00
}
2016-05-01 11:57:23 +02:00
/**
* Detect <enter> key
* @param {Event} evt
*/
detectKey(evt) {
if (!this.enterKey) {
return;
}
if (evt) {
2016-06-02 06:13:56 +02:00
let key = keyCode(evt);
2016-05-08 03:48:51 +02:00
if (key === ENTER_KEY) {
2016-05-01 11:57:23 +02:00
this.filter();
2016-06-02 06:13:56 +02:00
cancelEvt(evt);
stopEvt(evt);
2016-05-01 11:57:23 +02:00
} else {
this.isUserTyping = true;
2016-05-08 07:26:52 +02:00
root.clearInterval(this.autoFilterTimer);
2016-05-01 11:57:23 +02:00
this.autoFilterTimer = null;
}
}
}
/**
* Filter's keyup event: if auto-filter on, detect user is typing and filter
* columns
* @param {Event} evt
*/
onKeyUp(evt) {
if (!this.autoFilter) {
return;
}
2016-06-02 06:13:56 +02:00
let key = keyCode(evt);
2016-05-01 11:57:23 +02:00
this.isUserTyping = false;
function filter() {
2016-05-08 07:26:52 +02:00
root.clearInterval(this.autoFilterTimer);
2016-05-01 11:57:23 +02:00
this.autoFilterTimer = null;
if (!this.isUserTyping) {
this.filter();
this.isUserTyping = null;
}
}
2016-05-08 03:48:51 +02:00
if (key !== ENTER_KEY && key !== TAB_KEY && key !== ESC_KEY &&
key !== UP_ARROW_KEY && key !== DOWN_ARROW_KEY) {
2016-05-01 11:57:23 +02:00
if (this.autoFilterTimer === null) {
2016-05-08 07:26:52 +02:00
this.autoFilterTimer = root.setInterval(filter.bind(this),
2016-05-01 11:57:23 +02:00
this.autoFilterDelay);
}
} else {
2016-05-08 07:26:52 +02:00
root.clearInterval(this.autoFilterTimer);
2016-05-01 11:57:23 +02:00
this.autoFilterTimer = null;
}
}
/**
* Filter's keydown event: if auto-filter on, detect user is typing
*/
onKeyDown() {
if (this.autoFilter) {
this.isUserTyping = true;
}
}
/**
* Filter's focus event
* @param {Event} evt
*/
onInpFocus(evt) {
2016-06-02 06:13:56 +02:00
let elm = targetEvt(evt);
2016-05-01 11:57:23 +02:00
this.emitter.emit('filter-focus', this, elm);
}
/**
* Filter's blur event: if auto-filter on, clear interval on filter blur
*/
onInpBlur() {
if (this.autoFilter) {
this.isUserTyping = false;
2016-05-08 07:26:52 +02:00
root.clearInterval(this.autoFilterTimer);
2016-05-01 11:57:23 +02:00
}
this.emitter.emit('filter-blur', this);
}
2016-01-09 03:42:33 +01:00
/**
* Insert filters row at initialization
*/
2016-04-30 04:58:15 +02:00
_insertFiltersRow() {
if (this.gridLayout) {
2016-01-04 07:59:30 +01:00
return;
}
let fltrow;
2016-05-24 10:42:11 +02:00
let thead = tag(this.tbl, 'thead');
2016-04-30 04:58:15 +02:00
if (thead.length > 0) {
2016-01-04 07:59:30 +01:00
fltrow = thead[0].insertRow(this.filtersRowIndex);
} else {
fltrow = this.tbl.insertRow(this.filtersRowIndex);
}
fltrow.className = this.fltsRowCssClass;
2016-04-30 04:58:15 +02:00
if (this.isExternalFlt) {
fltrow.style.display = NONE;
2016-01-04 07:59:30 +01:00
}
this.emitter.emit('filters-row-inserted', this, fltrow);
return fltrow;
}
2016-01-09 03:42:33 +01:00
/**
* Initialize filtersless table
*/
2016-04-30 04:58:15 +02:00
_initNoFilters() {
if (this.fltGrid) {
2016-01-04 07:59:30 +01:00
return;
}
2016-04-30 04:58:15 +02:00
this.refRow = this.refRow > 0 ? this.refRow - 1 : 0;
2016-01-04 07:59:30 +01:00
this.nbFilterableRows = this.getRowsNb();
}
2016-01-09 03:42:33 +01:00
/**
* Build input filter type
* @param {Number} colIndex Column index
* @param {String} cssClass Css class applied to filter
* @param {DOMElement} container Container DOM element
*/
2016-04-30 04:58:15 +02:00
_buildInputFilter(colIndex, cssClass, container) {
2016-01-08 07:44:22 +01:00
let col = this.getFilterType(colIndex);
let externalFltTgtId = this.isExternalFlt ?
this.externalFltTgtIds[colIndex] : null;
let inptype = col === INPUT ? 'text' : 'hidden';
2016-05-24 10:42:11 +02:00
let inp = createElm(INPUT,
2016-04-30 04:58:15 +02:00
['id', this.prfxFlt + colIndex + '_' + this.id],
2016-01-08 07:44:22 +01:00
['type', inptype], ['ct', colIndex]);
2016-04-30 04:58:15 +02:00
if (inptype !== 'hidden' && this.watermark) {
2016-01-08 07:44:22 +01:00
inp.setAttribute('placeholder',
this.isWatermarkArray ? (this.watermark[colIndex] || '') :
this.watermark
);
}
inp.className = cssClass || this.fltCssClass;
2016-06-02 06:13:56 +02:00
addEvt(inp, 'focus', (evt) => this.onInpFocus(evt));
2016-01-08 07:44:22 +01:00
//filter is appended in custom element
2016-04-30 04:58:15 +02:00
if (externalFltTgtId) {
2016-05-25 09:31:53 +02:00
elm(externalFltTgtId).appendChild(inp);
2016-01-08 07:44:22 +01:00
this.externalFltEls.push(inp);
} else {
container.appendChild(inp);
}
this.fltIds.push(inp.id);
2016-01-08 07:44:22 +01:00
2016-06-02 06:13:56 +02:00
addEvt(inp, 'keypress', (evt) => this.detectKey(evt));
addEvt(inp, 'keydown', () => this.onKeyDown());
addEvt(inp, 'keyup', (evt) => this.onKeyUp(evt));
addEvt(inp, 'blur', () => this.onInpBlur());
2016-01-08 07:44:22 +01:00
}
2016-01-09 03:42:33 +01:00
/**
* Build submit button
* @param {Number} colIndex Column index
* @param {DOMElement} container Container DOM element
*/
2016-04-30 04:58:15 +02:00
_buildSubmitButton(colIndex, container) {
2016-01-09 03:42:33 +01:00
let externalFltTgtId = this.isExternalFlt ?
this.externalFltTgtIds[colIndex] : null;
2016-05-24 10:42:11 +02:00
let btn = createElm(INPUT,
2016-04-30 04:58:15 +02:00
['id', this.prfxValButton + colIndex + '_' + this.id],
2016-01-09 03:42:33 +01:00
['type', 'button'], ['value', this.btnText]);
btn.className = this.btnCssClass;
//filter is appended in custom element
2016-04-30 04:58:15 +02:00
if (externalFltTgtId) {
2016-05-25 09:31:53 +02:00
elm(externalFltTgtId).appendChild(btn);
2016-04-12 18:39:20 +02:00
} else {
2016-01-09 03:42:33 +01:00
container.appendChild(btn);
}
2016-06-02 06:13:56 +02:00
addEvt(btn, 'click', () => this.filter());
2016-01-09 03:42:33 +01:00
}
/**
* Return a feature instance for a given name
* @param {String} name Name of the feature
* @return {Object}
*/
2016-04-30 04:58:15 +02:00
feature(name) {
return this.Mod[name];
}
/**
* Initialise all the extensions defined in the configuration object
*/
2016-04-30 04:58:15 +02:00
initExtensions() {
2015-05-30 14:23:33 +02:00
let exts = this.extensions;
// Set config's publicPath dynamically for Webpack...
__webpack_public_path__ = this.basePath;
2015-02-22 12:46:05 +01:00
2016-01-02 15:33:31 +01:00
this.emitter.emit('before-loading-extensions', this);
2016-04-30 04:58:15 +02:00
for (let i = 0, len = exts.length; i < len; i++) {
2015-05-30 14:23:33 +02:00
let ext = exts[i];
2016-04-30 04:58:15 +02:00
if (!this.ExtRegistry[ext.name]) {
2015-04-27 16:26:59 +02:00
this.loadExtension(ext);
}
2015-02-22 12:46:05 +01:00
}
2016-01-02 15:33:31 +01:00
this.emitter.emit('after-loading-extensions', this);
2015-02-22 12:46:05 +01:00
}
/**
* Load an extension module
* @param {Object} ext Extension config object
*/
2016-04-30 04:58:15 +02:00
loadExtension(ext) {
if (!ext || !ext.name) {
2015-02-22 12:46:05 +01:00
return;
}
2015-05-30 14:23:33 +02:00
let name = ext.name;
let path = ext.path;
let modulePath;
2015-05-13 12:54:29 +02:00
2016-04-30 04:58:15 +02:00
if (name && path) {
2015-05-15 16:26:21 +02:00
modulePath = ext.path + name;
2015-05-13 12:54:29 +02:00
} else {
name = name.replace('.js', '');
2015-07-04 16:34:07 +02:00
modulePath = 'extensions/{}/{}'.replace(/{}/g, name);
2015-05-13 12:54:29 +02:00
}
// Require pattern for Webpack
2016-04-30 04:58:15 +02:00
require(['./' + modulePath], (mod) => {
2016-02-22 08:14:58 +01:00
/* eslint-disable */
2015-12-25 15:33:04 +01:00
let inst = new mod.default(this, ext);
2016-02-22 08:14:58 +01:00
/* eslint-enable */
2015-05-13 12:54:29 +02:00
inst.init();
this.ExtRegistry[name] = inst;
2015-04-27 16:26:59 +02:00
});
}
/**
* Get an extension instance
* @param {String} name Name of the extension
* @return {Object} Extension instance
*/
2016-04-30 04:58:15 +02:00
extension(name) {
return this.ExtRegistry[name];
}
/**
* Check passed extension name exists
* @param {String} name Name of the extension
* @return {Boolean}
*/
2016-04-30 04:58:15 +02:00
hasExtension(name) {
2016-05-15 04:56:12 +02:00
return !isEmpty(this.ExtRegistry[name]);
}
2015-05-28 12:09:34 +02:00
/**
* Destroy all the extensions defined in the configuration object
*/
2016-04-30 04:58:15 +02:00
destroyExtensions() {
2015-05-30 14:23:33 +02:00
let exts = this.extensions;
2015-05-28 12:09:34 +02:00
2016-04-30 04:58:15 +02:00
for (let i = 0, len = exts.length; i < len; i++) {
2015-05-30 14:23:33 +02:00
let ext = exts[i];
let extInstance = this.ExtRegistry[ext.name];
2016-04-30 04:58:15 +02:00
if (extInstance) {
2015-05-28 12:09:34 +02:00
extInstance.destroy();
this.ExtRegistry[ext.name] = undefined;
2015-05-28 12:09:34 +02:00
}
}
}
/**
* Load themes defined in the configuration object
*/
2016-04-30 04:58:15 +02:00
loadThemes() {
2015-05-30 14:23:33 +02:00
let themes = this.themes;
2016-01-02 15:33:31 +01:00
this.emitter.emit('before-loading-themes', this);
2015-02-22 12:46:05 +01:00
//Default theme config
2016-04-30 04:58:15 +02:00
if (this.enableDefaultTheme) {
2015-05-30 14:23:33 +02:00
let defaultTheme = { name: 'default' };
2015-05-15 16:26:21 +02:00
this.themes.push(defaultTheme);
2015-02-22 12:46:05 +01:00
}
2016-05-15 04:56:12 +02:00
if (isArray(themes)) {
2016-04-30 04:58:15 +02:00
for (let i = 0, len = themes.length; i < len; i++) {
2015-05-30 14:23:33 +02:00
let theme = themes[i];
let name = theme.name;
let path = theme.path;
2015-06-27 16:47:13 +02:00
let styleId = this.prfxTf + name;
2016-04-30 04:58:15 +02:00
if (name && !path) {
2015-05-15 16:26:21 +02:00
path = this.themesPath + name + '/' + name + '.css';
2015-02-22 12:46:05 +01:00
}
2016-04-30 04:58:15 +02:00
else if (!name && theme.path) {
2015-05-15 16:26:21 +02:00
name = 'theme{0}'.replace('{0}', i);
}
2016-04-30 04:58:15 +02:00
if (!this.isImported(path, 'link')) {
2015-06-27 16:47:13 +02:00
this.import(styleId, path, null, 'link');
2015-02-22 12:46:05 +01:00
}
}
}
2015-06-27 16:47:13 +02:00
//Some elements need to be overriden for default theme
2015-02-22 12:46:05 +01:00
//Reset button
this.btnResetText = null;
this.btnResetHtml = '<input type="button" value="" class="' +
2016-04-30 04:58:15 +02:00
this.btnResetCssClass + '" title="Clear filters" />';
2015-02-22 12:46:05 +01:00
//Paging buttons
this.btnPrevPageHtml = '<input type="button" value="" class="' +
2016-04-30 04:58:15 +02:00
this.btnPageCssClass + ' previousPage" title="Previous page" />';
2015-02-22 12:46:05 +01:00
this.btnNextPageHtml = '<input type="button" value="" class="' +
2016-04-30 04:58:15 +02:00
this.btnPageCssClass + ' nextPage" title="Next page" />';
2015-02-22 12:46:05 +01:00
this.btnFirstPageHtml = '<input type="button" value="" class="' +
2016-04-30 04:58:15 +02:00
this.btnPageCssClass + ' firstPage" title="First page" />';
2015-02-22 12:46:05 +01:00
this.btnLastPageHtml = '<input type="button" value="" class="' +
2016-04-30 04:58:15 +02:00
this.btnPageCssClass + ' lastPage" title="Last page" />';
2015-02-22 12:46:05 +01:00
//Loader
this.loader = true;
this.loaderHtml = '<div class="defaultLoader"></div>';
this.loaderText = null;
2016-01-02 15:33:31 +01:00
this.emitter.emit('after-loading-themes', this);
2015-02-22 12:46:05 +01:00
}
2015-06-27 16:47:13 +02:00
/**
* Return stylesheet DOM element for a given theme name
* @return {DOMElement} stylesheet element
*/
2016-04-30 04:58:15 +02:00
getStylesheet(name = 'default') {
2016-05-25 09:31:53 +02:00
return elm(this.prfxTf + name);
2015-06-27 16:47:13 +02:00
}
/**
* Destroy filter grid
*/
2016-04-30 04:58:15 +02:00
destroy() {
2016-05-29 12:44:27 +02:00
if (!this.initialized) {
return;
}
let Mod = this.Mod;
let emitter = this.emitter;
2015-06-06 12:06:15 +02:00
2016-04-30 04:58:15 +02:00
if (this.isExternalFlt && !this.popupFilters) {
this.removeExternalFlts();
}
2016-04-30 04:58:15 +02:00
if (this.infDiv) {
this.removeToolbar();
}
2016-04-30 04:58:15 +02:00
if (this.markActiveColumns) {
this.clearActiveColumns();
2016-04-30 04:58:15 +02:00
emitter.off(['before-filtering'], () => this.clearActiveColumns());
emitter.off(['cell-processed'],
2016-04-30 04:58:15 +02:00
(tf, colIndex) => this.markActiveColumn(colIndex));
}
2016-04-30 04:58:15 +02:00
if (this.hasExtensions) {
2015-05-28 12:09:34 +02:00
this.destroyExtensions();
}
2015-05-28 12:09:34 +02:00
this.validateAllRows();
2016-04-30 04:58:15 +02:00
if (this.fltGrid && !this.gridLayout) {
this.tbl.deleteRow(this.filtersRowIndex);
}
2015-06-06 12:06:15 +02:00
// broadcast destroy event
emitter.emit('destroy', this);
2015-06-27 16:47:13 +02:00
// Destroy modules
2016-01-17 07:56:15 +01:00
// TODO: subcribe modules to destroy event instead
2016-04-30 04:58:15 +02:00
Object.keys(Mod).forEach(function (key) {
2016-05-07 14:08:43 +02:00
let feature = Mod[key];
2016-05-15 04:56:12 +02:00
if (feature && isFn(feature.destroy)) {
2015-06-05 15:10:25 +02:00
feature.destroy();
}
});
// unsubscribe to events
2016-04-30 04:58:15 +02:00
if (this.hasVisibleRows) {
emitter.off(['after-filtering'], () => this.enforceVisibility());
}
2016-04-30 04:58:15 +02:00
if (this.linkedFilters) {
emitter.off(['after-filtering'], () => this.linkFilters());
}
2016-04-05 10:51:56 +02:00
this.emitter.off(['filter-focus'],
2016-04-30 04:58:15 +02:00
(tf, filter) => this.setActiveFilterId(filter.id));
2016-05-24 10:42:11 +02:00
removeClass(this.tbl, this.prfxTf);
removeClass(this.tbl, this.prfxResponsive);
2015-11-21 08:31:32 +01:00
this.nbHiddenRows = 0;
2015-12-31 02:56:50 +01:00
this.validRowsIndex = [];
this.fltIds = [];
2016-01-17 07:56:15 +01:00
this.initialized = false;
2015-02-22 12:46:05 +01:00
}
/**
* Generate container element for paging, reset button, rows counter etc.
*/
2016-04-30 04:58:15 +02:00
setToolbar() {
if (this.infDiv) {
2015-02-22 12:46:05 +01:00
return;
}
/*** container div ***/
2016-05-24 10:42:11 +02:00
let infdiv = createElm('div', ['id', this.prfxInfDiv + this.id]);
2015-02-22 12:46:05 +01:00
infdiv.className = this.infDivCssClass;
//custom container
2016-04-30 04:58:15 +02:00
if (this.toolBarTgtId) {
2016-05-25 09:31:53 +02:00
elm(this.toolBarTgtId).appendChild(infdiv);
2015-02-22 12:46:05 +01:00
}
//grid-layout
2016-04-30 04:58:15 +02:00
else if (this.gridLayout) {
let gridLayout = this.Mod.gridLayout;
2015-04-24 12:38:20 +02:00
gridLayout.tblMainCont.appendChild(infdiv);
2016-06-05 10:44:21 +02:00
infdiv.className = gridLayout.infDivCssClass;
2015-02-22 12:46:05 +01:00
}
//default location: just above the table
2016-04-30 04:58:15 +02:00
else {
2016-05-24 10:42:11 +02:00
let cont = createElm('caption');
2015-06-05 15:10:25 +02:00
cont.appendChild(infdiv);
this.tbl.insertBefore(cont, this.tbl.firstChild);
2015-02-22 12:46:05 +01:00
}
2016-05-25 09:31:53 +02:00
this.infDiv = elm(this.prfxInfDiv + this.id);
2015-02-22 12:46:05 +01:00
/*** left div containing rows # displayer ***/
2016-05-24 10:42:11 +02:00
let ldiv = createElm('div', ['id', this.prfxLDiv + this.id]);
2015-02-22 12:46:05 +01:00
ldiv.className = this.lDivCssClass;
infdiv.appendChild(ldiv);
2016-05-25 09:31:53 +02:00
this.lDiv = elm(this.prfxLDiv + this.id);
2015-02-22 12:46:05 +01:00
/*** right div containing reset button
+ nb results per page select ***/
2016-05-24 10:42:11 +02:00
let rdiv = createElm('div', ['id', this.prfxRDiv + this.id]);
2015-02-22 12:46:05 +01:00
rdiv.className = this.rDivCssClass;
infdiv.appendChild(rdiv);
2016-05-25 09:31:53 +02:00
this.rDiv = elm(this.prfxRDiv + this.id);
2015-02-22 12:46:05 +01:00
/*** mid div containing paging elements ***/
2016-05-24 10:42:11 +02:00
let mdiv = createElm('div', ['id', this.prfxMDiv + this.id]);
2015-02-22 12:46:05 +01:00
mdiv.className = this.mDivCssClass;
infdiv.appendChild(mdiv);
2016-05-25 09:31:53 +02:00
this.mDiv = elm(this.prfxMDiv + this.id);
2015-02-22 12:46:05 +01:00
2016-01-19 13:13:34 +01:00
// emit help initialisation only if undefined
2016-05-15 04:56:12 +02:00
if (isUndef(this.help)) {
// explicitily set enabled field to true to initialise help by
// default, only if setting is undefined
this.Mod.help.enabled = true;
2016-01-19 13:13:34 +01:00
this.emitter.emit('init-help', this);
2015-05-28 15:44:23 +02:00
}
2015-02-22 12:46:05 +01:00
}
/**
* Remove toolbar container element
*/
2016-04-30 04:58:15 +02:00
removeToolbar() {
if (!this.infDiv) {
2015-02-22 12:46:05 +01:00
return;
}
2016-05-24 10:42:11 +02:00
removeElm(this.infDiv);
2015-02-22 12:46:05 +01:00
this.infDiv = null;
2015-06-06 12:06:15 +02:00
let tbl = this.tbl;
2016-05-24 10:42:11 +02:00
let captions = tag(tbl, 'caption');
2016-04-30 04:58:15 +02:00
if (captions.length > 0) {
[].forEach.call(captions, (elm) => tbl.removeChild(elm));
2015-06-06 12:06:15 +02:00
}
2015-02-22 12:46:05 +01:00
}
/**
* Remove all the external column filters
*/
2016-04-30 04:58:15 +02:00
removeExternalFlts() {
if (!this.isExternalFlt) {
2015-02-22 12:46:05 +01:00
return;
}
2015-05-30 14:23:33 +02:00
let ids = this.externalFltTgtIds,
len = ids.length;
2016-04-30 04:58:15 +02:00
for (let ct = 0; ct < len; ct++) {
2015-05-30 14:23:33 +02:00
let externalFltTgtId = ids[ct],
2016-05-25 09:31:53 +02:00
externalFlt = elm(externalFltTgtId);
2016-04-30 04:58:15 +02:00
if (externalFlt) {
2015-02-22 12:46:05 +01:00
externalFlt.innerHTML = '';
}
}
}
/**
* Check if given column implements a filter with custom options
* @param {Number} colIndex Column's index
* @return {Boolean}
*/
isCustomOptions(colIndex) {
return this.hasCustomOptions &&
2016-05-15 05:33:16 +02:00
this.customOptions.cols.indexOf(colIndex) !== -1;
}
/**
* Returns an array [[value0, value1 ...],[text0, text1 ...]] with the
* custom options values and texts
* @param {Number} colIndex Column's index
* @return {Array}
*/
2016-04-30 04:58:15 +02:00
getCustomOptions(colIndex) {
2016-05-15 04:56:12 +02:00
if (isEmpty(colIndex) || !this.isCustomOptions(colIndex)) {
return;
}
let customOptions = this.customOptions;
let cols = customOptions.cols;
let optTxt = [], optArray = [];
let index = cols.indexOf(colIndex);
let slcValues = customOptions.values[index];
let slcTexts = customOptions.texts[index];
let slcSort = customOptions.sorts[index];
2016-04-30 04:58:15 +02:00
for (let r = 0, len = slcValues.length; r < len; r++) {
optArray.push(slcValues[r]);
2016-04-30 04:58:15 +02:00
if (slcTexts[r]) {
optTxt.push(slcTexts[r]);
} else {
optTxt.push(slcValues[r]);
}
}
2016-04-30 04:58:15 +02:00
if (slcSort) {
optArray.sort();
optTxt.sort();
}
return [optArray, optTxt];
}
2015-02-22 12:46:05 +01:00
/**
2015-05-24 11:16:09 +02:00
* Filter the table by retrieving the data from each cell in every single
* row and comparing it to the search term for current column. A row is
2015-06-05 09:46:48 +02:00
* hidden when all the search terms are not found in inspected row.
*/
2016-04-30 04:58:15 +02:00
filter() {
2016-05-29 12:44:27 +02:00
if (!this.fltGrid || !this.initialized) {
2015-02-22 12:46:05 +01:00
return;
}
//invoke onbefore callback
2016-04-30 04:58:15 +02:00
if (this.onBeforeFilter) {
2015-02-22 12:46:05 +01:00
this.onBeforeFilter.call(null, this);
}
this.emitter.emit('before-filtering', this);
2015-02-22 12:46:05 +01:00
2015-05-30 14:23:33 +02:00
let row = this.tbl.rows,
nbRows = this.getRowsNb(true),
hiddenRows = 0;
2015-05-28 15:44:23 +02:00
2015-02-22 12:46:05 +01:00
this.validRowsIndex = [];
// search args re-init
2016-02-01 08:11:45 +01:00
let searchArgs = this.getFiltersValue();
2015-02-22 12:46:05 +01:00
let numCellData;
let nbFormat;
let re_le = new RegExp(this.leOperator),
2015-02-22 12:46:05 +01:00
re_ge = new RegExp(this.geOperator),
re_l = new RegExp(this.lwOperator),
re_g = new RegExp(this.grOperator),
re_d = new RegExp(this.dfOperator),
2016-05-20 10:08:39 +02:00
re_lk = new RegExp(rgxEsc(this.lkOperator)),
2015-02-22 12:46:05 +01:00
re_eq = new RegExp(this.eqOperator),
re_st = new RegExp(this.stOperator),
re_en = new RegExp(this.enOperator),
// re_an = new RegExp(this.anOperator),
// re_cr = new RegExp(this.curExp),
2015-02-22 12:46:05 +01:00
re_em = this.emOperator,
re_nm = this.nmOperator,
2016-05-20 10:08:39 +02:00
re_re = new RegExp(rgxEsc(this.rgxOperator));
2015-02-22 12:46:05 +01:00
//keyword highlighting
2016-04-30 04:58:15 +02:00
function highlight(str, ok, cell) {
/*jshint validthis:true */
2016-04-30 04:58:15 +02:00
if (this.highlightKeywords && ok) {
2015-05-31 13:48:29 +02:00
str = str.replace(re_lk, '');
str = str.replace(re_eq, '');
str = str.replace(re_st, '');
str = str.replace(re_en, '');
2015-05-30 14:23:33 +02:00
let w = str;
2016-04-30 04:58:15 +02:00
if (re_le.test(str) || re_ge.test(str) || re_l.test(str) ||
re_g.test(str) || re_d.test(str)) {
2016-05-24 10:42:11 +02:00
w = getText(cell);
2015-02-22 12:46:05 +01:00
}
2016-04-30 04:58:15 +02:00
if (w !== '') {
2016-01-18 07:36:10 +01:00
this.emitter.emit('highlight-keyword', this, cell, w);
2015-02-22 12:46:05 +01:00
}
}
}
//looks for search argument in current row
2016-04-30 04:58:15 +02:00
function hasArg(sA, cellData, j) {
2016-05-20 10:08:39 +02:00
sA = matchCase(sA, this.caseSensitive);
2016-05-20 09:21:42 +02:00
let occurence;
2016-05-07 14:08:43 +02:00
let dtType = this.hasColDateType ?
this.colDateType[j] : this.defaultDateType;
2015-02-22 12:46:05 +01:00
//Search arg operator tests
2015-05-30 14:23:33 +02:00
let hasLO = re_l.test(sA),
2015-02-22 12:46:05 +01:00
hasLE = re_le.test(sA),
hasGR = re_g.test(sA),
hasGE = re_ge.test(sA),
hasDF = re_d.test(sA),
hasEQ = re_eq.test(sA),
hasLK = re_lk.test(sA),
// hasAN = re_an.test(sA),
2015-02-22 12:46:05 +01:00
hasST = re_st.test(sA),
hasEN = re_en.test(sA),
hasEM = (re_em === sA),
hasNM = (re_nm === sA),
hasRE = re_re.test(sA);
//Search arg dates tests
2016-05-21 03:33:49 +02:00
let isLDate = hasLO && isValidDate(sA.replace(re_l, ''), dtType);
let isLEDate = hasLE && isValidDate(sA.replace(re_le, ''), dtType);
let isGDate = hasGR && isValidDate(sA.replace(re_g, ''), dtType);
let isGEDate = hasGE && isValidDate(sA.replace(re_ge, ''), dtType);
let isDFDate = hasDF && isValidDate(sA.replace(re_d, ''), dtType);
let isEQDate = hasEQ && isValidDate(sA.replace(re_eq, ''), dtType);
2015-05-30 14:23:33 +02:00
let dte1, dte2;
2015-02-22 12:46:05 +01:00
//dates
2016-05-21 03:33:49 +02:00
if (isValidDate(cellData, dtType)) {
dte1 = formatDate(cellData, dtType);
2015-02-22 12:46:05 +01:00
// lower date
2016-04-30 04:58:15 +02:00
if (isLDate) {
2016-05-21 03:33:49 +02:00
dte2 = formatDate(sA.replace(re_l, ''), dtType);
2015-02-22 12:46:05 +01:00
occurence = dte1 < dte2;
}
// lower equal date
2016-04-30 04:58:15 +02:00
else if (isLEDate) {
2016-05-21 03:33:49 +02:00
dte2 = formatDate(sA.replace(re_le, ''), dtType);
2015-02-22 12:46:05 +01:00
occurence = dte1 <= dte2;
}
// greater equal date
2016-04-30 04:58:15 +02:00
else if (isGEDate) {
2016-05-21 03:33:49 +02:00
dte2 = formatDate(sA.replace(re_ge, ''), dtType);
2015-02-22 12:46:05 +01:00
occurence = dte1 >= dte2;
}
// greater date
2016-04-30 04:58:15 +02:00
else if (isGDate) {
2016-05-21 03:33:49 +02:00
dte2 = formatDate(sA.replace(re_g, ''), dtType);
2015-02-22 12:46:05 +01:00
occurence = dte1 > dte2;
}
// different date
2016-04-30 04:58:15 +02:00
else if (isDFDate) {
2016-05-21 03:33:49 +02:00
dte2 = formatDate(sA.replace(re_d, ''), dtType);
2016-05-15 05:33:16 +02:00
occurence = dte1.toString() !== dte2.toString();
2015-02-22 12:46:05 +01:00
}
// equal date
2016-04-30 04:58:15 +02:00
else if (isEQDate) {
2016-05-21 03:33:49 +02:00
dte2 = formatDate(sA.replace(re_eq, ''), dtType);
2016-05-15 05:33:16 +02:00
occurence = dte1.toString() === dte2.toString();
2015-02-22 12:46:05 +01:00
}
// searched keyword with * operator doesn't have to be a date
2016-04-30 04:58:15 +02:00
else if (re_lk.test(sA)) {// like date
2016-05-20 10:08:39 +02:00
occurence = contains(sA.replace(re_lk, ''), cellData,
2015-12-05 14:37:59 +01:00
false, this.caseSensitive);
2015-02-22 12:46:05 +01:00
}
2016-05-21 03:33:49 +02:00
else if (isValidDate(sA, dtType)) {
dte2 = formatDate(sA, dtType);
2015-12-05 14:37:59 +01:00
occurence = dte1.toString() === dte2.toString();
2015-02-22 12:46:05 +01:00
}
//empty
2016-04-30 04:58:15 +02:00
else if (hasEM) {
2016-05-20 10:08:39 +02:00
occurence = isEmptyString(cellData);
2015-02-22 12:46:05 +01:00
}
//non-empty
2016-04-30 04:58:15 +02:00
else if (hasNM) {
2016-05-20 10:08:39 +02:00
occurence = !isEmptyString(cellData);
2015-12-21 07:25:25 +01:00
} else {
2016-05-20 10:08:39 +02:00
occurence = contains(sA, cellData, this.isExactMatch(j),
2015-12-21 07:25:25 +01:00
this.caseSensitive);
2015-02-22 12:46:05 +01:00
}
}
2016-04-30 04:58:15 +02:00
else {
2015-02-22 12:46:05 +01:00
//first numbers need to be formated
2016-04-30 04:58:15 +02:00
if (this.hasColNbFormat && this.colNbFormat[j]) {
numCellData = removeNbFormat(cellData, this.colNbFormat[j]);
nbFormat = this.colNbFormat[j];
2015-02-22 12:46:05 +01:00
} else {
2016-04-30 04:58:15 +02:00
if (this.thousandsSeparator === ',' &&
this.decimalSeparator === '.') {
2015-12-05 14:37:59 +01:00
numCellData = removeNbFormat(cellData, 'us');
2015-02-22 12:46:05 +01:00
nbFormat = 'us';
} else {
2015-12-05 14:37:59 +01:00
numCellData = removeNbFormat(cellData, 'eu');
2015-02-22 12:46:05 +01:00
nbFormat = 'eu';
}
}
// first checks if there is any operator (<,>,<=,>=,!,*,=,{,},
// rgx:)
// lower equal
2016-04-30 04:58:15 +02:00
if (hasLE) {
2015-12-05 14:37:59 +01:00
occurence = numCellData <= removeNbFormat(
2015-06-05 15:10:25 +02:00
sA.replace(re_le, ''), nbFormat);
2015-02-22 12:46:05 +01:00
}
//greater equal
2016-04-30 04:58:15 +02:00
else if (hasGE) {
2015-12-05 14:37:59 +01:00
occurence = numCellData >= removeNbFormat(
2015-06-05 15:10:25 +02:00
sA.replace(re_ge, ''), nbFormat);
2015-02-22 12:46:05 +01:00
}
//lower
2016-04-30 04:58:15 +02:00
else if (hasLO) {
2015-12-05 14:37:59 +01:00
occurence = numCellData < removeNbFormat(
2015-06-05 15:10:25 +02:00
sA.replace(re_l, ''), nbFormat);
2015-02-22 12:46:05 +01:00
}
//greater
2016-04-30 04:58:15 +02:00
else if (hasGR) {
2015-12-05 14:37:59 +01:00
occurence = numCellData > removeNbFormat(
2015-06-05 15:10:25 +02:00
sA.replace(re_g, ''), nbFormat);
2015-02-22 12:46:05 +01:00
}
//different
2016-04-30 04:58:15 +02:00
else if (hasDF) {
2016-05-20 10:08:39 +02:00
occurence = contains(sA.replace(re_d, ''), cellData,
2015-12-05 14:37:59 +01:00
false, this.caseSensitive) ? false : true;
2015-02-22 12:46:05 +01:00
}
//like
2016-04-30 04:58:15 +02:00
else if (hasLK) {
2016-05-20 10:08:39 +02:00
occurence = contains(sA.replace(re_lk, ''), cellData,
2015-12-05 14:37:59 +01:00
false, this.caseSensitive);
2015-02-22 12:46:05 +01:00
}
//equal
2016-04-30 04:58:15 +02:00
else if (hasEQ) {
2016-05-20 10:08:39 +02:00
occurence = contains(sA.replace(re_eq, ''), cellData,
2015-12-05 14:37:59 +01:00
true, this.caseSensitive);
2015-02-22 12:46:05 +01:00
}
//starts with
2016-04-30 04:58:15 +02:00
else if (hasST) {
2015-12-05 14:37:59 +01:00
occurence = cellData.indexOf(sA.replace(re_st, '')) === 0 ?
2015-02-22 12:46:05 +01:00
true : false;
}
//ends with
2016-04-30 04:58:15 +02:00
else if (hasEN) {
2015-06-05 15:10:25 +02:00
let searchArg = sA.replace(re_en, '');
2015-02-22 12:46:05 +01:00
occurence =
2016-04-30 04:58:15 +02:00
cellData.lastIndexOf(searchArg, cellData.length - 1) ===
(cellData.length - 1) - (searchArg.length - 1) &&
cellData.lastIndexOf(searchArg, cellData.length - 1)
> -1 ? true : false;
2015-02-22 12:46:05 +01:00
}
//empty
2016-04-30 04:58:15 +02:00
else if (hasEM) {
2016-05-20 10:08:39 +02:00
occurence = isEmptyString(cellData);
2015-02-22 12:46:05 +01:00
}
//non-empty
2016-04-30 04:58:15 +02:00
else if (hasNM) {
2016-05-20 10:08:39 +02:00
occurence = !isEmptyString(cellData);
2015-02-22 12:46:05 +01:00
}
//regexp
2016-04-30 04:58:15 +02:00
else if (hasRE) {
2015-02-22 12:46:05 +01:00
//in case regexp fires an exception
2016-04-30 04:58:15 +02:00
try {
2015-02-22 12:46:05 +01:00
//operator is removed
2016-04-30 04:58:15 +02:00
let srchArg = sA.replace(re_re, '');
2015-05-30 14:23:33 +02:00
let rgx = new RegExp(srchArg);
2015-12-05 14:37:59 +01:00
occurence = rgx.test(cellData);
} catch (ex) {
occurence = false;
}
} else {
// If numeric type data, perform a strict equality test and
// fallback to unformatted number string comparison
2016-05-19 04:35:27 +02:00
if (numCellData && this.hasColNbFormat &&
2016-05-19 04:50:18 +02:00
this.colNbFormat[j] && !this.singleSearchFlt) {
sA = removeNbFormat(sA, nbFormat);
occurence = numCellData === sA ||
2016-05-20 10:08:39 +02:00
contains(sA.toString(), numCellData.toString(),
this.isExactMatch(j), this.caseSensitive);
} else {
// Finally test search term is contained in cell data
2016-05-20 10:08:39 +02:00
occurence = contains(sA, cellData, this.isExactMatch(j),
this.caseSensitive);
}
2015-02-22 12:46:05 +01:00
}
}//else
return occurence;
}//fn
for (let k = this.refRow; k < nbRows; k++) {
// already filtered rows display re-init
row[k].style.display = '';
2015-02-22 12:46:05 +01:00
2016-04-12 18:39:20 +02:00
let cells = row[k].cells,
nchilds = cells.length;
2015-02-22 12:46:05 +01:00
// checks if row has exact cell #
2016-04-30 04:58:15 +02:00
if (nchilds !== this.nbCells) {
2015-02-22 12:46:05 +01:00
continue;
}
2015-05-30 14:23:33 +02:00
let occurence = [],
2015-06-11 09:27:02 +02:00
isRowValid = true,
2015-02-22 12:46:05 +01:00
//only for single filter search
singleFltRowValid = false;
// this loop retrieves cell data
2016-04-30 04:58:15 +02:00
for (let j = 0; j < nchilds; j++) {
2015-02-22 12:46:05 +01:00
//searched keyword
2016-02-01 08:11:45 +01:00
let sA = searchArgs[this.singleSearchFlt ? 0 : j];
2016-04-30 04:58:15 +02:00
if (sA === '') {
2015-02-22 12:46:05 +01:00
continue;
}
2016-05-20 10:08:39 +02:00
let cellData = matchCase(this.getCellData(cells[j]),
this.caseSensitive);
2015-02-22 12:46:05 +01:00
//multiple search parameter operator ||
let sAOrSplit = sA.toString().split(this.orOperator),
2016-02-22 08:14:58 +01:00
//multiple search || parameter boolean
hasMultiOrSA = sAOrSplit.length > 1,
//multiple search parameter operator &&
sAAndSplit = sA.toString().split(this.anOperator),
//multiple search && parameter boolean
hasMultiAndSA = sAAndSplit.length > 1;
2015-02-22 12:46:05 +01:00
//detect operators or array query
2016-05-15 04:56:12 +02:00
if (isArray(sA) || hasMultiOrSA || hasMultiAndSA) {
2015-05-30 14:23:33 +02:00
let cS,
s,
occur = false;
2016-05-15 04:56:12 +02:00
if (isArray(sA)) {
s = sA;
} else {
2015-02-22 12:46:05 +01:00
s = hasMultiOrSA ? sAOrSplit : sAAndSplit;
}
// TODO: improve clarity/readability of this block
2016-04-30 04:58:15 +02:00
for (let w = 0, len = s.length; w < len; w++) {
2016-05-20 10:08:39 +02:00
cS = trim(s[w]);
2015-12-05 14:37:59 +01:00
occur = hasArg.call(this, cS, cellData, j);
2016-04-12 18:39:20 +02:00
highlight.call(this, cS, occur, cells[j]);
2016-04-30 04:58:15 +02:00
if ((hasMultiOrSA && occur) ||
(hasMultiAndSA && !occur)) {
2015-02-22 12:46:05 +01:00
break;
}
2016-05-15 04:56:12 +02:00
if (isArray(sA) && occur) {
2015-02-22 12:46:05 +01:00
break;
}
}
occurence[j] = occur;
2015-02-22 12:46:05 +01:00
}
//single search parameter
else {
2016-05-20 10:08:39 +02:00
occurence[j] = hasArg.call(this, trim(sA), cellData, j);
2016-04-12 18:39:20 +02:00
highlight.call(this, sA, occurence[j], cells[j]);
2015-02-22 12:46:05 +01:00
}//else single param
2016-04-30 04:58:15 +02:00
if (!occurence[j]) {
2015-06-11 09:27:02 +02:00
isRowValid = false;
2015-02-22 12:46:05 +01:00
}
2016-04-30 04:58:15 +02:00
if (this.singleSearchFlt && occurence[j]) {
2015-02-22 12:46:05 +01:00
singleFltRowValid = true;
}
2016-04-12 18:39:20 +02:00
this.emitter.emit('cell-processed', this, j, cells[j]);
2015-02-22 12:46:05 +01:00
}//for j
2016-04-30 04:58:15 +02:00
if (this.singleSearchFlt && singleFltRowValid) {
2015-02-22 12:46:05 +01:00
isRowValid = true;
}
2016-04-30 04:58:15 +02:00
if (!isRowValid) {
this.validateRow(k, false);
hiddenRows++;
2015-02-22 12:46:05 +01:00
} else {
this.validateRow(k, true);
2015-02-22 12:46:05 +01:00
}
this.emitter.emit('row-processed', this, k,
this.validRowsIndex.length, isRowValid);
2015-02-22 12:46:05 +01:00
}// for k
this.nbHiddenRows = hiddenRows;
2015-02-22 12:46:05 +01:00
//invokes onafterfilter callback
2016-04-30 04:58:15 +02:00
if (this.onAfterFilter) {
this.onAfterFilter.call(null, this);
2015-02-22 12:46:05 +01:00
}
2015-12-08 12:53:12 +01:00
2016-03-18 07:58:32 +01:00
this.emitter.emit('after-filtering', this, searchArgs);
2015-02-22 12:46:05 +01:00
}
/**
* Return the data of a specified column
* @param {Number} colIndex Column index
* @param {Boolean} includeHeaders Optional: include headers row
* @param {Boolean} num Optional: return unformatted number
* @param {Array} exclude Optional: list of row indexes to be excluded
* @return {Array} Flat list of data for a column
*/
2016-04-30 04:58:15 +02:00
getColValues(colIndex, includeHeaders = false, num = false, exclude = []) {
if (!this.fltGrid) {
2015-02-22 12:46:05 +01:00
return;
}
let row = this.tbl.rows;
let nbRows = this.getRowsNb(true);
let colValues = [];
2015-02-22 12:46:05 +01:00
2016-04-30 04:58:15 +02:00
if (includeHeaders) {
colValues.push(this.getHeadersText()[colIndex]);
}
for (let i = this.refRow; i < nbRows; i++) {
2015-05-30 14:23:33 +02:00
let isExludedRow = false;
2015-02-22 12:46:05 +01:00
// checks if current row index appears in exclude array
2016-04-30 04:58:15 +02:00
if (exclude.length > 0) {
2016-05-15 05:33:16 +02:00
isExludedRow = exclude.indexOf(i) !== -1;
2015-02-22 12:46:05 +01:00
}
2015-05-30 14:23:33 +02:00
let cell = row[i].cells,
2015-02-22 12:46:05 +01:00
nchilds = cell.length;
// checks if row has exact cell # and is not excluded
2016-04-30 04:58:15 +02:00
if (nchilds === this.nbCells && !isExludedRow) {
2015-02-22 12:46:05 +01:00
// this loop retrieves cell data
2016-04-30 04:58:15 +02:00
for (let j = 0; j < nchilds; j++) {
2016-05-15 05:33:16 +02:00
if (j !== colIndex || row[i].style.display !== '') {
2015-06-02 09:45:24 +02:00
continue;
2015-02-22 12:46:05 +01:00
}
2015-12-05 14:37:59 +01:00
let cellData = this.getCellData(cell[j]),
2015-06-02 09:45:24 +02:00
nbFormat = this.colNbFormat ?
2016-06-02 05:31:58 +02:00
this.colNbFormat[colIndex] : undefined,
2016-05-20 09:21:42 +02:00
data = num ? removeNbFormat(cellData, nbFormat) :
2016-04-30 04:58:15 +02:00
cellData;
2015-06-02 09:45:24 +02:00
colValues.push(data);
2015-02-22 12:46:05 +01:00
}
}
2015-05-30 14:23:33 +02:00
}
2015-02-22 12:46:05 +01:00
return colValues;
}
/**
* Return the filter's value of a specified column
* @param {Number} index Column index
* @return {String} Filter value
*/
2016-04-30 04:58:15 +02:00
getFilterValue(index) {
if (!this.fltGrid) {
2015-02-22 12:46:05 +01:00
return;
}
let fltValue = '';
let flt = this.getFilterElement(index);
2016-04-30 04:58:15 +02:00
if (!flt) {
return fltValue;
2015-02-22 12:46:05 +01:00
}
2015-07-12 11:50:55 +02:00
2015-06-05 15:10:25 +02:00
let fltColType = this.getFilterType(index);
if (fltColType !== MULTIPLE && fltColType !== CHECKLIST) {
2015-02-22 12:46:05 +01:00
fltValue = flt.value;
}
//mutiple select
else if (fltColType === MULTIPLE) {
fltValue = this.feature('dropdown').getValues(index);
2015-02-22 12:46:05 +01:00
}
//checklist
else if (fltColType === CHECKLIST) {
fltValue = this.feature('checkList').getValues(index);
}
//return an empty string if collection is empty or contains a single
//empty string
2016-05-15 04:56:12 +02:00
if (isArray(fltValue) && fltValue.length === 0 ||
(fltValue.length === 1 && fltValue[0] === '')) {
fltValue = '';
2015-02-22 12:46:05 +01:00
}
2015-02-22 12:46:05 +01:00
return fltValue;
}
/**
* Return the filters' values
* @return {Array} List of filters' values
*/
2016-04-30 04:58:15 +02:00
getFiltersValue() {
if (!this.fltGrid) {
2015-02-22 12:46:05 +01:00
return;
}
2015-05-30 14:23:33 +02:00
let searchArgs = [];
2016-04-30 04:58:15 +02:00
for (let i = 0, len = this.fltIds.length; i < len; i++) {
let fltValue = this.getFilterValue(i);
2016-05-15 04:56:12 +02:00
if (isArray(fltValue)) {
searchArgs.push(fltValue);
} else {
2016-05-20 10:08:39 +02:00
searchArgs.push(trim(fltValue));
}
2015-02-22 12:46:05 +01:00
}
return searchArgs;
}
/**
2016-04-08 18:19:52 +02:00
* Return the ID of a specified column's filter
* @param {Number} index Column's index
* @return {String} ID of the filter element
*/
2016-04-30 04:58:15 +02:00
getFilterId(index) {
if (!this.fltGrid) {
2015-02-22 12:46:05 +01:00
return;
}
return this.fltIds[index];
2015-02-22 12:46:05 +01:00
}
/**
* Return the list of ids of filters matching a specified type.
* Note: hidden filters are also returned
*
* @param {String} type Filter type string ('input', 'select', 'multiple',
* 'checklist')
* @param {Boolean} bool If true returns columns indexes instead of IDs
* @return {[type]} List of element IDs or column indexes
*/
2016-04-30 04:58:15 +02:00
getFiltersByType(type, bool) {
if (!this.fltGrid) {
2015-02-22 12:46:05 +01:00
return;
}
2015-05-30 14:23:33 +02:00
let arr = [];
2016-04-30 04:58:15 +02:00
for (let i = 0, len = this.fltIds.length; i < len; i++) {
2015-06-05 15:10:25 +02:00
let fltType = this.getFilterType(i);
2016-05-20 10:08:39 +02:00
if (fltType === type.toLowerCase()) {
2015-05-31 13:48:29 +02:00
let a = bool ? i : this.fltIds[i];
2015-02-22 12:46:05 +01:00
arr.push(a);
}
}
return arr;
}
/**
* Return the filter's DOM element for a given column
* @param {Number} index Column's index
* @return {DOMElement}
*/
2016-04-30 04:58:15 +02:00
getFilterElement(index) {
2015-05-30 14:23:33 +02:00
let fltId = this.fltIds[index];
2016-05-25 09:31:53 +02:00
return elm(fltId);
2015-02-22 12:46:05 +01:00
}
/**
* Return the number of cells for a given row index
* @param {Number} rowIndex Index of the row
* @return {Number} Number of cells
*/
2016-04-30 04:58:15 +02:00
getCellsNb(rowIndex = 0) {
2015-06-05 15:10:25 +02:00
let tr = this.tbl.rows[rowIndex];
2015-02-22 12:46:05 +01:00
return tr.cells.length;
}
/**
* Return the number of filterable rows starting from reference row if
* defined
* @param {Boolean} includeHeaders Include the headers row
* @return {Number} Number of filterable rows
*/
2016-06-13 11:17:13 +02:00
getRowsNb(includeHeaders) {
let s = isUndef(this.refRow) ? 0 : this.refRow;
let ntrs = this.tbl.rows.length;
2016-06-13 11:17:13 +02:00
if (includeHeaders) {
s = 0;
}
2016-04-30 04:58:15 +02:00
return parseInt(ntrs - s, 10);
2015-02-22 12:46:05 +01:00
}
/**
* Return the data of a given cell
* @param {DOMElement} cell Cell's DOM object
* @return {String}
*/
2016-04-30 04:58:15 +02:00
getCellData(cell) {
let idx = cell.cellIndex;
//Check for customCellData callback
2016-05-15 05:33:16 +02:00
if (this.customCellData &&
this.customCellDataCols.indexOf(idx) !== -1) {
return this.customCellData.call(null, this, cell, idx);
2015-02-22 12:46:05 +01:00
} else {
2016-05-24 10:42:11 +02:00
return getText(cell);
2015-02-22 12:46:05 +01:00
}
}
/**
* Return the table data with following format:
* [
* [rowIndex, [value0, value1...]],
* [rowIndex, [value0, value1...]]
* ]
* @param {Boolean} includeHeaders Optional: include headers row
* @param {Boolean} excludeHiddenCols Optional: exclude hidden columns
* @return {Array}
*
* TODO: provide an API returning data in JSON format
*/
2016-04-30 04:58:15 +02:00
getTableData(includeHeaders = false, excludeHiddenCols = false) {
let rows = this.tbl.rows;
let nbRows = this.getRowsNb(true);
let tblData = [];
2016-04-30 04:58:15 +02:00
if (includeHeaders) {
let headers = this.getHeadersText(excludeHiddenCols);
tblData.push([this.getHeadersRowIndex(), headers]);
}
for (let k = this.refRow; k < nbRows; k++) {
let rowData = [k, []];
let cells = rows[k].cells;
2016-04-30 04:58:15 +02:00
for (let j = 0, len = cells.length; j < len; j++) {
if (excludeHiddenCols && this.hasExtension('colsVisibility')) {
if (this.extension('colsVisibility').isColHidden(j)) {
continue;
}
}
let cellData = this.getCellData(cells[j]);
rowData[1].push(cellData);
2015-02-22 12:46:05 +01:00
}
tblData.push(rowData);
2015-02-22 12:46:05 +01:00
}
return tblData;
2015-02-22 12:46:05 +01:00
}
/**
* Return the filtered data with following format:
* [
* [rowIndex, [value0, value1...]],
* [rowIndex, [value0, value1...]]
* ]
* @param {Boolean} includeHeaders Optional: include headers row
* @param {Boolean} excludeHiddenCols Optional: exclude hidden columns
* @return {Array}
*
* TODO: provide an API returning data in JSON format
*/
2016-04-30 04:58:15 +02:00
getFilteredData(includeHeaders = false, excludeHiddenCols = false) {
if (!this.validRowsIndex) {
2015-02-22 12:46:05 +01:00
return [];
}
let rows = this.tbl.rows,
2015-02-22 12:46:05 +01:00
filteredData = [];
2016-04-30 04:58:15 +02:00
if (includeHeaders) {
let headers = this.getHeadersText(excludeHiddenCols);
filteredData.push([this.getHeadersRowIndex(), headers]);
2015-02-22 12:46:05 +01:00
}
2015-05-30 14:23:33 +02:00
let validRows = this.getValidRows(true);
2016-04-30 04:58:15 +02:00
for (let i = 0; i < validRows.length; i++) {
let rData = [this.validRowsIndex[i], []],
cells = rows[this.validRowsIndex[i]].cells;
2016-04-30 04:58:15 +02:00
for (let k = 0; k < cells.length; k++) {
if (excludeHiddenCols && this.hasExtension('colsVisibility')) {
if (this.extension('colsVisibility').isColHidden(k)) {
continue;
}
}
let cellData = this.getCellData(cells[k]);
rData[1].push(cellData);
2015-02-22 12:46:05 +01:00
}
filteredData.push(rData);
}
return filteredData;
}
/**
* Return the filtered data for a given column index
* @param {Number} colIndex Colmun's index
* @param {Boolean} includeHeaders Optional: include headers row
* @return {Array} Flat list of values ['val0','val1','val2'...]
*
* TODO: provide an API returning data in JSON format
*/
2016-04-30 04:58:15 +02:00
getFilteredDataCol(colIndex, includeHeaders = false) {
2016-05-15 04:56:12 +02:00
if (isUndef(colIndex)) {
2015-02-22 12:46:05 +01:00
return [];
}
2016-02-16 08:41:47 +01:00
let data = this.getFilteredData(),
2015-02-22 12:46:05 +01:00
colData = [];
2016-04-30 04:58:15 +02:00
if (includeHeaders) {
colData.push(this.getHeadersText()[colIndex]);
}
2016-04-30 04:58:15 +02:00
for (let i = 0, len = data.length; i < len; i++) {
2015-05-30 14:23:33 +02:00
let r = data[i],
2015-02-22 12:46:05 +01:00
//cols values of current row
d = r[1],
//data of searched column
c = d[colIndex];
colData.push(c);
}
return colData;
}
/**
* Get the display value of a row
2016-05-15 04:56:12 +02:00
* @param {HTMLTableRowElement} row DOM element of the row
* @return {String} Usually 'none' or ''
*/
2016-04-30 04:58:15 +02:00
getRowDisplay(row) {
2015-02-22 12:46:05 +01:00
return row.style.display;
}
/**
* Validate/invalidate row by setting the 'validRow' attribute on the row
* @param {Number} rowIndex Index of the row
* @param {Boolean} isValid
*/
2016-04-30 04:58:15 +02:00
validateRow(rowIndex, isValid) {
2015-05-30 14:23:33 +02:00
let row = this.tbl.rows[rowIndex];
2016-04-30 04:58:15 +02:00
if (!row || typeof isValid !== 'boolean') {
2015-02-22 12:46:05 +01:00
return;
}
// always visible rows are valid
2016-04-30 04:58:15 +02:00
if (this.hasVisibleRows && this.visibleRows.indexOf(rowIndex) !== -1) {
2015-02-22 12:46:05 +01:00
isValid = true;
}
let displayFlag = isValid ? '' : NONE,
2015-02-22 12:46:05 +01:00
validFlag = isValid ? 'true' : 'false';
row.style.display = displayFlag;
2016-04-30 04:58:15 +02:00
if (this.paging) {
row.setAttribute('validRow', validFlag);
2015-02-22 12:46:05 +01:00
}
2015-12-31 02:56:50 +01:00
2016-04-30 04:58:15 +02:00
if (isValid) {
if (this.validRowsIndex.indexOf(rowIndex) === -1) {
2015-12-31 02:56:50 +01:00
this.validRowsIndex.push(rowIndex);
}
2016-04-30 04:58:15 +02:00
if (this.onRowValidated) {
2015-12-31 02:56:50 +01:00
this.onRowValidated.call(null, this, rowIndex);
}
this.emitter.emit('row-validated', this, rowIndex);
}
2015-02-22 12:46:05 +01:00
}
/**
* Validate all filterable rows
*/
2016-04-30 04:58:15 +02:00
validateAllRows() {
2016-05-29 12:44:27 +02:00
if (!this.initialized) {
2015-02-22 12:46:05 +01:00
return;
}
this.validRowsIndex = [];
2016-04-30 04:58:15 +02:00
for (let k = this.refRow; k < this.nbFilterableRows; k++) {
2015-02-22 12:46:05 +01:00
this.validateRow(k, true);
}
}
/**
* Set search value to a given filter
* @param {Number} index Column's index
2016-02-28 13:01:26 +01:00
* @param {String or Array} query searcharg Search term
*/
2016-04-30 04:58:15 +02:00
setFilterValue(index, query = '') {
if (!this.fltGrid) {
2015-02-22 12:46:05 +01:00
return;
}
2015-05-30 14:23:33 +02:00
let slc = this.getFilterElement(index),
2015-06-05 09:46:48 +02:00
fltColType = this.getFilterType(index);
2015-02-22 12:46:05 +01:00
if (fltColType !== MULTIPLE && fltColType !== CHECKLIST) {
2016-04-30 04:58:15 +02:00
if (this.loadFltOnDemand && !this.initialized) {
2016-01-17 07:56:15 +01:00
this.emitter.emit('build-select-filter', this, index,
this.linkedFilters, this.isExternalFlt);
}
slc.value = query;
2015-02-22 12:46:05 +01:00
}
//multiple selects
else if (fltColType === MULTIPLE) {
2016-05-15 04:56:12 +02:00
let values = isArray(query) ? query :
2016-04-30 04:58:15 +02:00
query.split(' ' + this.orOperator + ' ');
2016-01-17 07:56:15 +01:00
2016-04-30 04:58:15 +02:00
if (this.loadFltOnDemand && !this.initialized) {
2016-01-17 07:56:15 +01:00
this.emitter.emit('build-select-filter', this, index,
this.linkedFilters, this.isExternalFlt);
}
2016-01-21 08:29:09 +01:00
this.emitter.emit('select-options', this, index, values);
2015-02-22 12:46:05 +01:00
}
//checklist
else if (fltColType === CHECKLIST) {
let values = [];
2016-04-30 04:58:15 +02:00
if (this.loadFltOnDemand && !this.initialized) {
2016-01-17 07:56:15 +01:00
this.emitter.emit('build-checklist-filter', this, index,
this.isExternalFlt);
}
2016-05-15 04:56:12 +02:00
if (isArray(query)) {
values = query;
} else {
2016-05-20 10:08:39 +02:00
query = matchCase(query, this.caseSensitive);
2016-04-30 04:58:15 +02:00
values = query.split(' ' + this.orOperator + ' ');
}
2016-01-17 07:56:15 +01:00
2016-01-20 07:14:32 +01:00
this.emitter.emit('select-checklist-options', this, index, values);
2015-02-22 12:46:05 +01:00
}
}
/**
* Set them columns' widths as per configuration
2015-06-06 12:06:15 +02:00
* @param {Element} tbl DOM element
*/
2016-04-30 04:58:15 +02:00
setColWidths(tbl) {
if (!this.hasColWidths) {
2015-02-22 12:46:05 +01:00
return;
}
2015-06-06 12:06:15 +02:00
tbl = tbl || this.tbl;
2016-06-10 15:03:46 +02:00
let nbCols = this.nbCells;
let colWidths = this.colWidths;
let colTags = tag(tbl, 'col');
let tblHasColTag = colTags.length > 0;
let frag = !tblHasColTag ? doc.createDocumentFragment() : null;
for (let k = 0; k < nbCols; k++) {
let col;
if (tblHasColTag) {
col = colTags[k];
} else {
col = createElm('col', ['id', this.id + '_col_' + k]);
frag.appendChild(col);
2015-02-22 12:46:05 +01:00
}
2016-06-10 15:03:46 +02:00
col.style.width = colWidths[k];
}
if (!tblHasColTag) {
tbl.insertBefore(frag, tbl.firstChild);
2015-02-22 12:46:05 +01:00
}
}
/**
* Makes defined rows always visible
*/
2016-04-30 04:58:15 +02:00
enforceVisibility() {
if (!this.hasVisibleRows) {
return;
}
let nbRows = this.getRowsNb(true);
2016-04-30 04:58:15 +02:00
for (let i = 0, len = this.visibleRows.length; i < len; i++) {
let row = this.visibleRows[i];
//row index cannot be > nrows
if (row <= nbRows) {
this.validateRow(row, true);
2015-02-22 12:46:05 +01:00
}
}
}
2015-05-26 09:29:30 +02:00
/**
* Clear all the filters' values
*/
2016-04-30 04:58:15 +02:00
clearFilters() {
if (!this.fltGrid) {
2015-02-22 12:46:05 +01:00
return;
}
2016-01-02 15:33:31 +01:00
this.emitter.emit('before-clearing-filters', this);
2016-04-30 04:58:15 +02:00
if (this.onBeforeReset) {
2015-02-22 12:46:05 +01:00
this.onBeforeReset.call(null, this, this.getFiltersValue());
}
2016-04-30 04:58:15 +02:00
for (let i = 0, len = this.fltIds.length; i < len; i++) {
2015-02-22 12:46:05 +01:00
this.setFilterValue(i, '');
}
2016-01-02 15:33:31 +01:00
this.filter();
2016-04-30 04:58:15 +02:00
if (this.onAfterReset) {
2016-04-05 10:51:56 +02:00
this.onAfterReset.call(null, this);
}
2016-01-02 15:33:31 +01:00
this.emitter.emit('after-clearing-filters', this);
2015-02-22 12:46:05 +01:00
}
2015-05-26 09:29:30 +02:00
/**
* Clears filtered columns visual indicator (background color)
*/
2016-04-30 04:58:15 +02:00
clearActiveColumns() {
for (let i = 0, len = this.getCellsNb(this.headersRow); i < len; i++) {
2016-05-24 10:42:11 +02:00
removeClass(this.getHeaderElement(i), this.activeColumnsCssClass);
2015-02-22 12:46:05 +01:00
}
}
/**
* Mark currently filtered column
* @param {Number} colIndex Column index
*/
2016-04-30 04:58:15 +02:00
markActiveColumn(colIndex) {
let header = this.getHeaderElement(colIndex);
2016-05-24 10:42:11 +02:00
if (hasClass(header, this.activeColumnsCssClass)) {
return;
}
2016-04-30 04:58:15 +02:00
if (this.onBeforeActiveColumn) {
this.onBeforeActiveColumn.call(null, this, colIndex);
}
2016-05-24 10:42:11 +02:00
addClass(header, this.activeColumnsCssClass);
2016-04-30 04:58:15 +02:00
if (this.onAfterActiveColumn) {
this.onAfterActiveColumn.call(null, this, colIndex);
}
}
/**
* Return the ID of the current active filter
* @returns {String}
*/
2016-04-30 04:58:15 +02:00
getActiveFilterId() {
2016-04-05 10:51:56 +02:00
return this.activeFilterId;
}
/**
* Set the ID of the current active filter
* @param {String} filterId Element ID
*/
2016-04-30 04:58:15 +02:00
setActiveFilterId(filterId) {
2016-04-05 10:51:56 +02:00
this.activeFilterId = filterId;
}
/**
* Return the column index for a given filter ID
* @param {string} [filterId=''] Filter ID
* @returns {Number} Column index
*/
2016-04-30 04:58:15 +02:00
getColumnIndexFromFilterId(filterId = '') {
let idx = filterId.split('_')[0];
idx = idx.split(this.prfxFlt)[1];
return parseInt(idx, 10);
}
2016-04-08 18:19:52 +02:00
/**
* Make specified column's filter active
* @param colIndex Index of a column
*/
2016-04-30 04:58:15 +02:00
activateFilter(colIndex) {
2016-05-15 04:56:12 +02:00
if (isUndef(colIndex)) {
2016-04-08 18:19:52 +02:00
return;
}
this.setActiveFilterId(this.getFilterId(colIndex));
}
2015-05-26 09:29:30 +02:00
/**
* Refresh the filters subject to linking ('select', 'multiple',
* 'checklist' type)
*/
2016-04-30 04:58:15 +02:00
linkFilters() {
if (!this.linkedFilters || !this.activeFilterId) {
return;
}
let slcA1 = this.getFiltersByType(SELECT, true),
slcA2 = this.getFiltersByType(MULTIPLE, true),
slcA3 = this.getFiltersByType(CHECKLIST, true),
2015-02-22 12:46:05 +01:00
slcIndex = slcA1.concat(slcA2);
slcIndex = slcIndex.concat(slcA3);
let activeIdx = this.getColumnIndexFromFilterId(this.activeFilterId);
2016-04-07 12:13:54 +02:00
2016-04-30 04:58:15 +02:00
for (let i = 0, len = slcIndex.length; i < len; i++) {
2016-05-25 09:31:53 +02:00
let curSlc = elm(this.fltIds[slcIndex[i]]);
2016-04-07 12:13:54 +02:00
let slcSelectedValue = this.getFilterValue(slcIndex[i]);
// Welcome to cyclomatic complexity hell :)
// TODO: simplify/refactor if statement
2016-04-30 04:58:15 +02:00
if (activeIdx !== slcIndex[i] ||
2016-05-15 05:33:16 +02:00
(this.paging && slcA1.indexOf(slcIndex[i]) !== -1 &&
2016-04-30 04:58:15 +02:00
activeIdx === slcIndex[i]) ||
2016-05-15 05:33:16 +02:00
(!this.paging && (slcA3.indexOf(slcIndex[i]) !== -1 ||
slcA2.indexOf(slcIndex[i]) !== -1)) ||
2016-04-30 04:58:15 +02:00
slcSelectedValue === this.displayAllText) {
2015-05-26 09:29:30 +02:00
//1st option needs to be inserted
2016-04-30 04:58:15 +02:00
if (this.loadFltOnDemand) {
2016-05-24 10:42:11 +02:00
let opt0 = createOpt(this.displayAllText, '');
curSlc.innerHTML = '';
curSlc.appendChild(opt0);
2015-05-26 09:29:30 +02:00
}
2015-02-22 12:46:05 +01:00
2016-05-15 05:33:16 +02:00
if (slcA3.indexOf(slcIndex[i]) !== -1) {
2016-01-19 13:13:34 +01:00
this.emitter.emit('build-checklist-filter', this,
2016-03-11 06:55:42 +01:00
slcIndex[i]);
2015-05-26 09:29:30 +02:00
} else {
2016-01-19 13:13:34 +01:00
this.emitter.emit('build-select-filter', this, slcIndex[i],
2016-03-11 06:55:42 +01:00
true);
2015-02-22 12:46:05 +01:00
}
2015-05-26 09:29:30 +02:00
2015-06-10 12:53:20 +02:00
this.setFilterValue(slcIndex[i], slcSelectedValue);
2015-05-26 09:29:30 +02:00
}
}
2015-02-22 12:46:05 +01:00
}
/**
* Determines if passed filter column implements exact query match
* @param {Number} colIndex [description]
* @return {Boolean} [description]
*/
2016-04-30 04:58:15 +02:00
isExactMatch(colIndex) {
let fltType = this.getFilterType(colIndex);
return this.exactMatchByCol[colIndex] || this.exactMatch ||
fltType !== INPUT;
2015-02-22 12:46:05 +01:00
}
2015-05-27 09:58:02 +02:00
/**
* Check if passed script or stylesheet is already imported
* @param {String} filePath Ressource path
* @param {String} type Possible values: 'script' or 'link'
* @return {Boolean}
*/
2016-06-10 15:03:46 +02:00
isImported(filePath, type = 'script') {
2015-05-30 14:23:33 +02:00
let imported = false,
2016-06-10 15:03:46 +02:00
attr = type === 'script' ? 'src' : 'href',
files = tag(doc, type);
2016-04-30 04:58:15 +02:00
for (let i = 0, len = files.length; i < len; i++) {
2016-06-10 15:03:46 +02:00
if (isUndef(files[i][attr])) {
2015-02-28 10:27:28 +01:00
continue;
}
2016-04-30 04:58:15 +02:00
if (files[i][attr].match(filePath)) {
2015-02-28 10:27:28 +01:00
imported = true;
break;
}
}
return imported;
}
2015-05-27 09:58:02 +02:00
/**
* Import script or stylesheet
* @param {String} fileId Ressource ID
* @param {String} filePath Ressource path
* @param {Function} callback Callback
* @param {String} type Possible values: 'script' or 'link'
*/
2016-06-10 15:03:46 +02:00
import(fileId, filePath, callback, type = 'script') {
if (this.isImported(filePath, type)) {
2015-02-22 12:46:05 +01:00
return;
}
2015-05-30 14:23:33 +02:00
let o = this,
2015-02-22 12:46:05 +01:00
isLoaded = false,
file,
2016-05-24 10:42:11 +02:00
head = tag(doc, 'head')[0];
2015-02-22 12:46:05 +01:00
2016-06-10 15:03:46 +02:00
if (type.toLowerCase() === 'link') {
2016-05-24 10:42:11 +02:00
file = createElm('link',
2015-02-22 12:46:05 +01:00
['id', fileId], ['type', 'text/css'],
['rel', 'stylesheet'], ['href', filePath]
);
} else {
2016-05-24 10:42:11 +02:00
file = createElm('script',
['id', fileId],
2015-02-22 12:46:05 +01:00
['type', 'text/javascript'], ['src', filePath]
);
}
//Browser <> IE onload event works only for scripts, not for stylesheets
2016-06-10 15:03:46 +02:00
file.onload = file.onreadystatechange = () => {
2016-04-30 04:58:15 +02:00
if (!isLoaded &&
2015-02-22 12:46:05 +01:00
(!this.readyState || this.readyState === 'loaded' ||
2016-04-30 04:58:15 +02:00
this.readyState === 'complete')) {
2015-02-22 12:46:05 +01:00
isLoaded = true;
2016-04-30 04:58:15 +02:00
if (typeof callback === 'function') {
2015-02-22 12:46:05 +01:00
callback.call(null, o);
}
}
};
2016-04-30 04:58:15 +02:00
file.onerror = function () {
2016-06-10 15:03:46 +02:00
throw new Error(`TableFilter could not load: ${filePath}`);
2015-02-22 12:46:05 +01:00
};
head.appendChild(file);
}
2015-05-27 09:58:02 +02:00
/**
* Check if table has filters grid
* @return {Boolean}
*/
isInitialized() {
2016-05-29 12:44:27 +02:00
return this.initialized;
2015-02-22 12:46:05 +01:00
}
2015-05-27 09:58:02 +02:00
/**
* Get list of filter IDs
* @return {[type]} [description]
*/
2016-04-30 04:58:15 +02:00
getFiltersId() {
2015-05-27 09:58:02 +02:00
return this.fltIds || [];
2015-02-22 12:46:05 +01:00
}
2015-05-27 09:58:02 +02:00
/**
* Get filtered (valid) rows indexes
* @param {Boolean} reCalc Force calculation of filtered rows list
* @return {Array} List of row indexes
*/
2016-04-30 04:58:15 +02:00
getValidRows(reCalc) {
if (!reCalc) {
2015-02-22 12:46:05 +01:00
return this.validRowsIndex;
}
let nbRows = this.getRowsNb(true);
2015-02-22 12:46:05 +01:00
this.validRowsIndex = [];
for (let k = this.refRow; k < nbRows; k++) {
2015-05-30 14:23:33 +02:00
let r = this.tbl.rows[k];
2016-04-30 04:58:15 +02:00
if (!this.paging) {
if (this.getRowDisplay(r) !== NONE) {
2015-02-22 12:46:05 +01:00
this.validRowsIndex.push(r.rowIndex);
}
} else {
2016-04-30 04:58:15 +02:00
if (r.getAttribute('validRow') === 'true' ||
r.getAttribute('validRow') === null) {
2015-02-22 12:46:05 +01:00
this.validRowsIndex.push(r.rowIndex);
}
}
}
return this.validRowsIndex;
}
2015-05-27 09:58:02 +02:00
/**
* Get the index of the row containing the filters
* @return {Number}
*/
2016-04-30 04:58:15 +02:00
getFiltersRowIndex() {
2015-02-22 12:46:05 +01:00
return this.filtersRowIndex;
}
2015-05-27 09:58:02 +02:00
/**
* Get the index of the headers row
* @return {Number}
*/
2016-04-30 04:58:15 +02:00
getHeadersRowIndex() {
2015-02-22 12:46:05 +01:00
return this.headersRow;
}
2015-05-27 09:58:02 +02:00
/**
* Get the row index from where the filtering process start (1st filterable
* row)
* @return {Number}
*/
2016-04-30 04:58:15 +02:00
getStartRowIndex() {
2015-02-22 12:46:05 +01:00
return this.refRow;
}
2015-05-27 09:58:02 +02:00
/**
* Get the index of the last row
* @return {Number}
*/
2016-04-30 04:58:15 +02:00
getLastRowIndex() {
let nbRows = this.getRowsNb(true);
return (nbRows - 1);
2015-02-22 12:46:05 +01:00
}
2015-05-27 09:58:02 +02:00
/**
* Get the header DOM element for a given column index
* @param {Number} colIndex Column index
* @return {Element}
2015-05-27 09:58:02 +02:00
*/
2016-04-30 04:58:15 +02:00
getHeaderElement(colIndex) {
let table = this.gridLayout ? this.Mod.gridLayout.headTbl : this.tbl;
2016-05-24 10:42:11 +02:00
let tHead = tag(table, 'thead');
2015-05-30 14:23:33 +02:00
let headersRow = this.headersRow;
let header;
2016-04-30 04:58:15 +02:00
for (let i = 0; i < this.nbCells; i++) {
if (i !== colIndex) {
2015-02-22 12:46:05 +01:00
continue;
}
2016-04-30 04:58:15 +02:00
if (tHead.length === 0) {
2015-04-04 10:10:09 +02:00
header = table.rows[headersRow].cells[i];
2015-02-22 12:46:05 +01:00
}
2016-04-30 04:58:15 +02:00
if (tHead.length === 1) {
2015-04-04 10:10:09 +02:00
header = tHead[0].rows[headersRow].cells[i];
2015-02-22 12:46:05 +01:00
}
break;
}
return header;
}
/**
* Return the list of headers' text
* @param {Boolean} excludeHiddenCols Optional: exclude hidden columns
* @return {Array} list of headers' text
*/
2016-04-30 04:58:15 +02:00
getHeadersText(excludeHiddenCols = false) {
let headers = [];
2016-04-30 04:58:15 +02:00
for (let j = 0; j < this.nbCells; j++) {
if (excludeHiddenCols && this.hasExtension('colsVisibility')) {
if (this.extension('colsVisibility').isColHidden(j)) {
continue;
}
}
let header = this.getHeaderElement(j);
2016-05-24 10:42:11 +02:00
let headerText = getFirstTextNode(header);
headers.push(headerText);
}
return headers;
}
2015-05-27 09:58:02 +02:00
/**
2015-06-05 09:46:48 +02:00
* Return the filter type for a specified column
* @param {Number} colIndex Column's index
* @return {String}
2015-05-27 09:58:02 +02:00
*/
2016-04-30 04:58:15 +02:00
getFilterType(colIndex) {
let colType = this.cfg['col_' + colIndex];
2016-05-20 10:08:39 +02:00
return !colType ? INPUT : colType.toLowerCase();
2015-02-22 12:46:05 +01:00
}
2015-05-27 09:58:02 +02:00
/**
* Get the total number of filterable rows
* @return {Number}
*/
2016-04-30 04:58:15 +02:00
getFilterableRowsNb() {
2015-02-22 12:46:05 +01:00
return this.getRowsNb(false);
}
2015-06-05 09:46:48 +02:00
/**
* Return the total number of valid rows
* @param {Boolean} [reCalc=false] Forces calculation of filtered rows
* @returns {Number}
*/
getValidRowsNb(reCalc = false) {
return this.getValidRows(reCalc).length;
}
2015-06-05 09:46:48 +02:00
/**
* Get the configuration object (literal object)
* @return {Object}
*/
2016-04-30 04:58:15 +02:00
config() {
2015-06-05 09:46:48 +02:00
return this.cfg;
}
2015-02-28 10:27:28 +01:00
}