1
0
Fork 0
mirror of https://github.com/koalyptus/TableFilter.git synced 2024-05-17 22:06:41 +02:00
TableFilter/doc/file/src/tablefilter.js.html
2015-08-02 18:27:59 +10:00

2834 lines
103 KiB
HTML

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<base data-ice="baseUrl" href="../../">
<title data-ice="title">src/tablefilter.js | TableFilter v0.0.0 API Document</title>
<link type="text/css" rel="stylesheet" href="css/style.css">
<link type="text/css" rel="stylesheet" href="css/prettify-tomorrow.css">
<script src="script/prettify/prettify.js"></script>
</head>
<body class="layout-container">
<header>
<a href="./">Home</a>
<a href="identifiers.html">Identifier</a>
<a href="source.html">Source</a>
<a data-ice="repoURL" href="https://github.com/koalyptus/TableFilter.git" class="repo-url-github">Repository</a>
<div class="search-box">
<span>
<img src="./image/search.png">
<span class="search-input-edge"></span><input class="search-input"><span class="search-input-edge"></span>
</span>
<ul class="search-result"></ul>
</div>
</header>
<nav class="navigation" data-ice="nav"><div data-ice="classWrap">
<h2>Class</h2>
<ul>
<li data-ice="classDoc"><span><a href="class/src/extensions/advancedGrid/adapterEzEditTable.js~AdapterEzEditTable.html">AdapterEzEditTable</a></span></li>
<li data-ice="classDoc"><span><a href="class/src/extensions/sort/adapterSortabletable.js~AdapterSortableTable.html">AdapterSortableTable</a></span></li>
<li data-ice="classDoc"><span><a href="class/src/modules/alternateRows.js~AlternateRows.html">AlternateRows</a></span></li>
<li data-ice="classDoc"><span><a href="class/src/modules/checkList.js~CheckList.html">CheckList</a></span></li>
<li data-ice="classDoc"><span><a href="class/src/modules/clearButton.js~ClearButton.html">ClearButton</a></span></li>
<li data-ice="classDoc"><span><a href="class/src/extensions/colOps/colOps.js~ColOps.html">ColOps</a></span></li>
<li data-ice="classDoc"><span><a href="class/src/extensions/colsVisibility/colsVisibility.js~ColsVisibility.html">ColsVisibility</a></span></li>
<li data-ice="classDoc"><span><a href="class/src/modules/dropdown.js~Dropdown.html">Dropdown</a></span></li>
<li data-ice="classDoc"><span><a href="class/src/extensions/filtersVisibility/filtersVisibility.js~FiltersVisibility.html">FiltersVisibility</a></span></li>
<li data-ice="classDoc"><span><a href="class/src/modules/gridLayout.js~GridLayout.html">GridLayout</a></span></li>
<li data-ice="classDoc"><span><a href="class/src/modules/help.js~Help.html">Help</a></span></li>
<li data-ice="classDoc"><span><a href="class/src/modules/highlightKeywords.js~HighlightKeyword.html">HighlightKeyword</a></span></li>
<li data-ice="classDoc"><span><a href="class/src/modules/loader.js~Loader.html">Loader</a></span></li>
<li data-ice="classDoc"><span><a href="class/src/modules/paging.js~Paging.html">Paging</a></span></li>
<li data-ice="classDoc"><span><a href="class/src/modules/popupFilter.js~PopupFilter.html">PopupFilter</a></span></li>
<li data-ice="classDoc"><span><a href="class/src/modules/rowsCounter.js~RowsCounter.html">RowsCounter</a></span></li>
<li data-ice="classDoc"><span><a href="class/src/modules/statusBar.js~StatusBar.html">StatusBar</a></span></li>
<li data-ice="classDoc"><span><a href="class/src/modules/store.js~Store.html">Store</a></span></li>
<li data-ice="classDoc"><span><a href="class/src/tablefilter.js~TableFilter.html">TableFilter</a></span></li>
</ul>
</div>
</nav>
<div class="content" data-ice="content"><h1 data-ice="title">src/tablefilter.js</h1>
<pre class="source-code line-number"><code class="prettyprint linenums" data-ice="content">import Event from &apos;./event&apos;;
import Dom from &apos;./dom&apos;;
import Str from &apos;./string&apos;;
import Cookie from &apos;./cookie&apos;;
import Types from &apos;./types&apos;;
import Arr from &apos;./array&apos;;
import DateHelper from &apos;./date&apos;;
import Helpers from &apos;./helpers&apos;;
// Features
import {Store} from &apos;./modules/store&apos;;
import {GridLayout} from &apos;./modules/gridLayout&apos;;
import {Loader} from &apos;./modules/loader&apos;;
import {HighlightKeyword} from &apos;./modules/highlightKeywords&apos;;
import {PopupFilter} from &apos;./modules/popupFilter&apos;;
import {Dropdown} from &apos;./modules/dropdown&apos;;
import {CheckList} from &apos;./modules/checkList&apos;;
import {RowsCounter} from &apos;./modules/rowsCounter&apos;;
import {StatusBar} from &apos;./modules/statusBar&apos;;
import {Paging} from &apos;./modules/paging&apos;;
import {ClearButton} from &apos;./modules/clearButton&apos;;
import {Help} from &apos;./modules/help&apos;;
import {AlternateRows} from &apos;./modules/alternateRows&apos;;
var global = window,
isValidDate = DateHelper.isValid,
formatDate = DateHelper.format,
doc = global.document;
export class TableFilter{
/**
* TF object constructor
* @param {String} id Table id
* @param {Number} row index indicating the 1st row
* @param {Object} configuration object
*
* TODO: Accept a TABLE element or query selectors
*/
constructor(id) {
if(arguments.length === 0){ return; }
this.id = id;
this.version = &apos;{VERSION}&apos;;
this.year = new Date().getFullYear();
this.tbl = Dom.id(id);
this.startRow = null;
this.refRow = null;
this.headersRow = null;
this.cfg = {};
this.nbFilterableRows = null;
this.nbRows = null;
this.nbCells = null;
this._hasGrid = false;
this.enableModules = false;
if(!this.tbl || this.tbl.nodeName != &apos;TABLE&apos; || this.getRowsNb() === 0){
throw new Error(
&apos;Could not instantiate TableFilter: HTML table not found.&apos;);
}
if(arguments.length &gt; 1){
for(let i=0, len=arguments.length; i&lt;len; i++){
let arg = arguments[i];
let argtype = typeof arg;
switch(Str.lower(argtype)){
case &apos;number&apos;:
this.startRow = arg;
break;
case &apos;object&apos;:
this.cfg = arg;
break;
}
}
}
// configuration object
let f = this.cfg;
//Start row et cols nb
this.refRow = this.startRow===null ? 2 : (this.startRow+1);
try{ this.nbCells = this.getCellsNb(this.refRow); }
catch(e){ this.nbCells = this.getCellsNb(0); }
//default script base path
this.basePath = f.base_path || &apos;tablefilter/&apos;;
/*** filter types ***/
this.fltTypeInp = &apos;input&apos;;
this.fltTypeSlc = &apos;select&apos;;
this.fltTypeMulti = &apos;multiple&apos;;
this.fltTypeCheckList = &apos;checklist&apos;;
this.fltTypeNone = &apos;none&apos;;
/*** filters&apos; grid properties ***/
//enables/disables filter grid
this.fltGrid = f.grid===false ? false : true;
/*** Grid layout ***/
//enables/disables grid layout (fixed headers)
this.gridLayout = Boolean(f.grid_layout);
this.sourceTblHtml = null;
if(this.gridLayout){
this.sourceTblHtml = this.tbl.outerHTML;
}
/*** ***/
this.filtersRowIndex = f.filters_row_index || 0;
this.headersRow = f.headers_row_index ||
(this.filtersRowIndex===0 ? 1 : 0);
if(this.gridLayout){
if(this.headersRow &gt; 1){
this.filtersRowIndex = this.headersRow+1;
} else {
this.filtersRowIndex = 1;
this.headersRow = 0;
}
}
//defines tag of the cells containing filters (td/th)
this.fltCellTag = f.filters_cell_tag!==&apos;th&apos; ||
f.filters_cell_tag!==&apos;td&apos; ? &apos;td&apos; : f.filters_cell_tag;
//stores filters ids
this.fltIds = [];
//stores filters DOM elements
this.fltElms = [];
//stores filters values
this.searchArgs = null;
//stores table data
this.tblData = [];
//stores valid rows indexes (rows visible upon filtering)
this.validRowsIndex = null;
//stores filters row element
this.fltGridEl = null;
//is first load boolean
this.isFirstLoad = true;
//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;
//defines css class for div containing paging elements, rows counter etc
this.infDivCssClass = f.inf_div_css_class || &apos;inf&apos;;
//defines css class for left div
this.lDivCssClass = f.left_div_css_class || &apos;ldiv&apos;;
//defines css class for right div
this.rDivCssClass = f.right_div_css_class || &apos;rdiv&apos;;
//defines css class for mid div
this.mDivCssClass = f.middle_div_css_class || &apos;mdiv&apos;;
//table container div css class
this.contDivCssClass = f.content_div_css_class || &apos;cont&apos;;
/*** filters&apos; grid appearance ***/
//stylesheet file
this.stylePath = f.style_path || this.basePath + &apos;style/&apos;;
this.stylesheet = f.stylesheet || this.stylePath+&apos;tablefilter.css&apos;;
this.stylesheetId = this.id + &apos;_style&apos;;
//defines css class for filters row
this.fltsRowCssClass = f.flts_row_css_class || &apos;fltrow&apos;;
//enables/disables icons (paging, reset button)
this.enableIcons = f.enable_icons===false ? false : true;
//enables/disbles rows alternating bg colors
this.alternateBgs = Boolean(f.alternate_rows);
//defines widths of columns
this.hasColWidths = Types.isArray(f.col_widths);
this.colWidths = this.hasColWidths ? f.col_widths : null;
//defines css class for filters
this.fltCssClass = f.flt_css_class || &apos;flt&apos;;
//defines css class for multiple selects filters
this.fltMultiCssClass = f.flt_multi_css_class || &apos;flt_multi&apos;;
//defines css class for filters
this.fltSmallCssClass = f.flt_small_css_class || &apos;flt_s&apos;;
//defines css class for single-filter
this.singleFltCssClass = f.single_flt_css_class || &apos;single_flt&apos;;
/*** filters&apos; grid behaviours ***/
//enables/disables enter key
this.enterKey = f.enter_key===false ? false : true;
//calls function before filtering starts
this.onBeforeFilter = Types.isFn(f.on_before_filter) ?
f.on_before_filter : null;
//calls function after filtering
this.onAfterFilter = Types.isFn(f.on_after_filter) ?
f.on_after_filter : null;
//enables/disables case sensitivity
this.caseSensitive = Boolean(f.case_sensitive);
//enables/disbles exact match for search
this.exactMatch = Boolean(f.exact_match);
//refreshes drop-down lists upon validation
this.linkedFilters = Boolean(f.linked_filters);
//wheter excluded options are disabled
this.disableExcludedOptions = Boolean(f.disable_excluded_options);
//stores active filter element
this.activeFlt = null;
//id of active filter
this.activeFilterId = null;
//enables always visible rows
this.hasVisibleRows = Boolean(f.rows_always_visible);
//array containing always visible rows
this.visibleRows = this.hasVisibleRows ? f.rows_always_visible : [];
//enables/disables external filters generation
this.isExternalFlt = Boolean(f.external_flt_grid);
//array containing ids of external elements containing filters
this.externalFltTgtIds = f.external_flt_grid_ids || null;
//stores filters elements if isExternalFlt is true
this.externalFltEls = [];
//delays any filtering process if loader true
this.execDelay = !isNaN(f.exec_delay) ? parseInt(f.exec_delay,10) : 100;
//calls function when filters grid loaded
this.onFiltersLoaded = Types.isFn(f.on_filters_loaded) ?
f.on_filters_loaded : null;
//enables/disables single filter search
this.singleSearchFlt = Boolean(f.single_search_filter);
//calls function after row is validated
this.onRowValidated = Types.isFn(f.on_row_validated) ?
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
this.customCellData = Types.isFn(f.custom_cell_data) ?
f.custom_cell_data : null;
//input watermark text array
this.watermark = f.watermark || &apos;&apos;;
this.isWatermarkArray = Types.isArray(this.watermark);
//id of toolbar container element
this.toolBarTgtId = f.toolbar_target_id || null;
//enables/disables help div
this.helpInstructions = Types.isUndef(f.help_instructions) ?
undefined : Boolean(f.help_instructions);
//popup filters
this.popUpFilters = Boolean(f.popup_filters);
//active columns color
this.markActiveColumns = Boolean(f.mark_active_columns);
//defines css class for active column header
this.activeColumnsCssClass = f.active_columns_css_class ||
&apos;activeHeader&apos;;
//calls function before active column header is marked
this.onBeforeActiveColumn = Types.isFn(f.on_before_active_column) ?
f.on_before_active_column : null;
//calls function after active column header is marked
this.onAfterActiveColumn = Types.isFn(f.on_after_active_column) ?
f.on_after_active_column : null;
/*** select filter&apos;s customisation and behaviours ***/
//defines 1st option text
this.displayAllText = f.display_all_text || &apos;Clear&apos;;
//enables/disables empty option in combo-box filters
this.enableEmptyOption = Boolean(f.enable_empty_option);
//defines empty option text
this.emptyText = f.empty_text || &apos;(Empty)&apos;;
//enables/disables non empty option in combo-box filters
this.enableNonEmptyOption = Boolean(f.enable_non_empty_option);
//defines empty option text
this.nonEmptyText = f.non_empty_text || &apos;(Non empty)&apos;;
//enables/disables onChange event on combo-box
this.onSlcChange = f.on_change===false ? false : true;
//enables/disables select options sorting
this.sortSlc = f.sort_select===false ? false : true;
//enables/disables ascending numeric options sorting
this.isSortNumAsc = Boolean(f.sort_num_asc);
this.sortNumAsc = this.isSortNumAsc ? f.sort_num_asc : null;
//enables/disables descending numeric options sorting
this.isSortNumDesc = Boolean(f.sort_num_desc);
this.sortNumDesc = this.isSortNumDesc ? f.sort_num_desc : null;
//enabled selects are populated on demand
this.fillSlcOnDemand = Boolean(f.fill_slc_on_demand);
this.hasCustomOptions = Types.isObj(f.custom_options);
this.customOptions = f.custom_options;
/*** Filter operators ***/
this.rgxOperator = f.regexp_operator || &apos;rgx:&apos;;
this.emOperator = f.empty_operator || &apos;[empty]&apos;;
this.nmOperator = f.nonempty_operator || &apos;[nonempty]&apos;;
this.orOperator = f.or_operator || &apos;||&apos;;
this.anOperator = f.and_operator || &apos;&amp;&amp;&apos;;
this.grOperator = f.greater_operator || &apos;&gt;&apos;;
this.lwOperator = f.lower_operator || &apos;&lt;&apos;;
this.leOperator = f.lower_equal_operator || &apos;&lt;=&apos;;
this.geOperator = f.greater_equal_operator || &apos;&gt;=&apos;;
this.dfOperator = f.different_operator || &apos;!&apos;;
this.lkOperator = f.like_operator || &apos;*&apos;;
this.eqOperator = f.equal_operator || &apos;=&apos;;
this.stOperator = f.start_with_operator || &apos;{&apos;;
this.enOperator = f.end_with_operator || &apos;}&apos;;
this.curExp = f.cur_exp || &apos;^[&#xA5;&#xA3;&#x20AC;$]&apos;;
this.separator = f.separator || &apos;,&apos;;
/*** rows counter ***/
//show/hides rows counter
this.rowsCounter = Boolean(f.rows_counter);
/*** status bar ***/
//show/hides status bar
this.statusBar = Boolean(f.status_bar);
/*** loader ***/
//enables/disables loader/spinner indicator
this.loader = Boolean(f.loader);
/*** validation - reset buttons/links ***/
//show/hides filter&apos;s validation button
this.displayBtn = Boolean(f.btn);
//defines validation button text
this.btnText = f.btn_text || (!this.enableIcons ? &apos;Go&apos; : &apos;&apos;);
//defines css class for validation button
this.btnCssClass = f.btn_css_class ||
(!this.enableIcons ? &apos;btnflt&apos; : &apos;btnflt_icon&apos;);
//show/hides reset link
this.btnReset = Boolean(f.btn_reset);
//defines css class for reset button
this.btnResetCssClass = f.btn_reset_css_class || &apos;reset&apos;;
//callback function before filters are cleared
this.onBeforeReset = Types.isFn(f.on_before_reset) ?
f.on_before_reset : null;
//callback function after filters are cleared
this.onAfterReset = Types.isFn(f.on_after_reset) ?
f.on_after_reset : null;
/*** paging ***/
//enables/disables table paging
this.paging = Boolean(f.paging);
this.nbVisibleRows = 0; //nb visible rows
this.nbHiddenRows = 0; //nb hidden rows
/*** autofilter on typing ***/
//enables/disables auto filtering, table is filtered when user stops
//typing
this.autoFilter = Boolean(f.auto_filter);
//onkeyup delay timer (msecs)
this.autoFilterDelay = !isNaN(f.auto_filter_delay) ?
f.auto_filter_delay : 900;
//typing indicator
this.isUserTyping = null;
this.autoFilterTimer = null;
/*** keyword highlighting ***/
//enables/disables keyword highlighting
this.highlightKeywords = Boolean(f.highlight_keywords);
/*** data types ***/
//defines default date type (european DMY)
this.defaultDateType = f.default_date_type || &apos;DMY&apos;;
//defines default thousands separator
//US = &apos;,&apos; EU = &apos;.&apos;
this.thousandsSeparator = f.thousands_separator || &apos;,&apos;;
//defines default decimal separator
//US &amp; javascript = &apos;.&apos; EU = &apos;,&apos;
this.decimalSeparator = f.decimal_separator || &apos;.&apos;;
//enables number format per column
this.hasColNbFormat = Types.isArray(f.col_number_format);
//array containing columns nb formats
this.colNbFormat = this.hasColNbFormat ? f.col_number_format : null;
//enables date type per column
this.hasColDateType = Types.isArray(f.col_date_type);
//array containing columns date type
this.colDateType = this.hasColDateType ? f.col_date_type : null;
/*** status messages ***/
//filtering
this.msgFilter = f.msg_filter || &apos;Filtering data...&apos;;
//populating drop-downs
this.msgPopulate = f.msg_populate || &apos;Populating filter...&apos;;
//populating drop-downs
this.msgPopulateCheckList = f.msg_populate_checklist ||
&apos;Populating list...&apos;;
//changing paging page
this.msgChangePage = f.msg_change_page || &apos;Collecting paging data...&apos;;
//clearing filters
this.msgClear = f.msg_clear || &apos;Clearing filters...&apos;;
//changing nb results/page
this.msgChangeResults = f.msg_change_results ||
&apos;Changing results per page...&apos;;
//re-setting grid values
this.msgResetValues = f.msg_reset_grid_values ||
&apos;Re-setting filters values...&apos;;
//re-setting page
this.msgResetPage = f.msg_reset_page || &apos;Re-setting page...&apos;;
//re-setting page length
this.msgResetPageLength = f.msg_reset_page_length ||
&apos;Re-setting page length...&apos;;
//table sorting
this.msgSort = f.msg_sort || &apos;Sorting data...&apos;;
//extensions loading
this.msgLoadExtensions = f.msg_load_extensions ||
&apos;Loading extensions...&apos;;
//themes loading
this.msgLoadThemes = f.msg_load_themes || &apos;Loading theme(s)...&apos;;
/*** ids prefixes ***/
//css class name added to table
this.prfxTf = &apos;TF&apos;;
//filters (inputs - selects)
this.prfxFlt = &apos;flt&apos;;
//validation button
this.prfxValButton = &apos;btn&apos;;
//container div for paging elements, rows counter etc.
this.prfxInfDiv = &apos;inf_&apos;;
//left div
this.prfxLDiv = &apos;ldiv_&apos;;
//right div
this.prfxRDiv = &apos;rdiv_&apos;;
//middle div
this.prfxMDiv = &apos;mdiv_&apos;;
//filter values cookie
this.prfxCookieFltsValues = &apos;tf_flts_&apos;;
//page nb cookie
this.prfxCookiePageNb = &apos;tf_pgnb_&apos;;
//page length cookie
this.prfxCookiePageLen = &apos;tf_pglen_&apos;;
/*** cookies ***/
this.hasStoredValues = false;
//remembers filters values on page load
this.rememberGridValues = Boolean(f.remember_grid_values);
//cookie storing filter values
this.fltsValuesCookie = this.prfxCookieFltsValues + this.id;
//remembers page nb on page load
this.rememberPageNb = this.paging &amp;&amp; f.remember_page_number;
//cookie storing page nb
this.pgNbCookie = this.prfxCookiePageNb + this.id;
//remembers page length on page load
this.rememberPageLen = this.paging &amp;&amp; f.remember_page_length;
//cookie storing page length
this.pgLenCookie = this.prfxCookiePageLen + this.id;
/*** extensions ***/
//imports external script
this.extensions = f.extensions;
this.hasExtensions = Types.isArray(this.extensions);
/*** themes ***/
this.enableDefaultTheme = Boolean(f.enable_default_theme);
//imports themes
this.hasThemes = (this.enableDefaultTheme || Types.isArray(f.themes));
this.themes = f.themes || [];
//themes path
this.themesPath = f.themes_path || this.stylePath + &apos;themes/&apos;;
// Features registry
this.Mod = {};
// Extensions registry
this.ExtRegistry = {};
/*** TF events ***/
this.Evt = {
name: {
filter: &apos;Filter&apos;,
dropdown: &apos;DropDown&apos;,
checklist: &apos;CheckList&apos;,
changepage: &apos;ChangePage&apos;,
clear: &apos;Clear&apos;,
changeresultsperpage: &apos;ChangeResults&apos;,
resetvalues: &apos;ResetValues&apos;,
resetpage: &apos;ResetPage&apos;,
resetpagelength: &apos;ResetPageLength&apos;,
loadextensions: &apos;LoadExtensions&apos;,
loadthemes: &apos;LoadThemes&apos;
},
// Detect &lt;enter&gt; key
detectKey(e) {
if(!this.enterKey){ return; }
let _ev = e || global.event;
if(_ev){
let key = Event.keyCode(_ev);
if(key===13){
this.filter();
Event.cancel(_ev);
Event.stop(_ev);
} else {
this.isUserTyping = true;
global.clearInterval(this.autoFilterTimer);
this.autoFilterTimer = null;
}
}
},
// if auto-filter on, detect user is typing and filter columns
onKeyUp(e) {
if(!this.autoFilter){
return;
}
let _ev = e || global.event;
let key = Event.keyCode(_ev);
this.isUserTyping = false;
function filter() {
/*jshint validthis:true */
global.clearInterval(this.autoFilterTimer);
this.autoFilterTimer = null;
if(!this.isUserTyping){
this.filter();
this.isUserTyping = null;
}
}
if(key!==13 &amp;&amp; key!==9 &amp;&amp; key!==27 &amp;&amp; key!==38 &amp;&amp; key!==40) {
if(this.autoFilterTimer === null){
this.autoFilterTimer = global.setInterval(
filter.bind(this), this.autoFilterDelay);
}
} else {
global.clearInterval(this.autoFilterTimer);
this.autoFilterTimer = null;
}
},
// if auto-filter on, detect user is typing
onKeyDown() {
if(!this.autoFilter) { return; }
this.isUserTyping = true;
},
// if auto-filter on, clear interval on filter blur
onInpBlur() {
if(this.autoFilter){
this.isUserTyping = false;
global.clearInterval(this.autoFilterTimer);
}
// TODO: hack to prevent ezEditTable enter key event hijaking.
// Needs to be fixed in the vendor&apos;s library
if(this.hasExtension(&apos;advancedGrid&apos;)){
var advGrid = this.extension(&apos;advancedGrid&apos;);
var ezEditTable = advGrid._ezEditTable;
if(advGrid.cfg.editable){
ezEditTable.Editable.Set();
}
if(advGrid.cfg.selection){
ezEditTable.Selection.Set();
}
}
},
// set focused text-box filter as active
onInpFocus(e) {
let _ev = e || global.event;
let elm = Event.target(_ev);
this.activeFilterId = elm.getAttribute(&apos;id&apos;);
this.activeFlt = Dom.id(this.activeFilterId);
if(this.popUpFilters){
Event.cancel(_ev);
Event.stop(_ev);
}
// TODO: hack to prevent ezEditTable enter key event hijaking.
// Needs to be fixed in the vendor&apos;s library
if(this.hasExtension(&apos;advancedGrid&apos;)){
var advGrid = this.extension(&apos;advancedGrid&apos;);
var ezEditTable = advGrid._ezEditTable;
if(advGrid.cfg.editable){
ezEditTable.Editable.Remove();
}
if(advGrid.cfg.selection){
ezEditTable.Selection.Remove();
}
}
},
// set focused drop-down filter as active
onSlcFocus(e) {
let _ev = e || global.event;
let elm = Event.target(_ev);
this.activeFilterId = elm.getAttribute(&apos;id&apos;);
this.activeFlt = Dom.id(this.activeFilterId);
// select is populated when element has focus
if(this.fillSlcOnDemand &amp;&amp; elm.getAttribute(&apos;filled&apos;) === &apos;0&apos;){
let ct = elm.getAttribute(&apos;ct&apos;);
this.Mod.dropdown._build(ct);
}
if(this.popUpFilters){
Event.cancel(_ev);
Event.stop(_ev);
}
},
// filter columns on drop-down filter change
onSlcChange(e) {
if(!this.activeFlt){ return; }
let _ev = e || global.event;
if(this.popUpFilters){ Event.stop(_ev); }
if(this.onSlcChange){ this.filter(); }
},
// fill checklist filter on click if required
onCheckListClick(e) {
let _ev = e || global.event;
let elm = Event.target(_ev);
if(this.fillSlcOnDemand &amp;&amp; elm.getAttribute(&apos;filled&apos;) === &apos;0&apos;){
let ct = elm.getAttribute(&apos;ct&apos;);
this.Mod.checkList._build(ct);
this.Mod.checkList.checkListDiv[ct].onclick = null;
this.Mod.checkList.checkListDiv[ct].title = &apos;&apos;;
}
},
// filter when validation button clicked
onBtnClick() {
this.filter();
}
};
}
/**
* Initialise filtering grid bar behaviours and layout
*
* TODO: decompose in smaller methods
*/
init(){
if(this._hasGrid){
return;
}
if(!this.tbl){
this.tbl = Dom.id(this.id);
}
if(this.gridLayout){
this.refRow = this.startRow===null ? 0 : this.startRow;
}
if(this.popUpFilters &amp;&amp;
((this.filtersRowIndex === 0 &amp;&amp; this.headersRow === 1) ||
this.gridLayout)){
this.headersRow = 0;
}
let Mod = this.Mod;
let n = this.singleSearchFlt ? 1 : this.nbCells,
inpclass;
//loads stylesheet if not imported
this.import(this.stylesheetId, this.stylesheet, null, &apos;link&apos;);
//loads theme
if(this.hasThemes){ this._loadThemes(); }
if(this.rememberGridValues || this.rememberPageNb ||
this.rememberPageLen){
Mod.store = new Store(this);
}
if(this.gridLayout){
Mod.gridLayout = new GridLayout(this);
Mod.gridLayout.init();
}
if(this.loader){
if(!Mod.loader){
Mod.loader = new Loader(this);
}
}
if(this.highlightKeywords){
Mod.highlightKeyword = new HighlightKeyword(this);
}
if(this.popUpFilters){
if(!Mod.popupFilter){
Mod.popupFilter = new PopupFilter(this);
}
Mod.popupFilter.init();
}
//filters grid is not generated
if(!this.fltGrid){
this.refRow = this.refRow-1;
if(this.gridLayout){
this.refRow = 0;
}
this.nbFilterableRows = this.getRowsNb();
this.nbVisibleRows = this.nbFilterableRows;
this.nbRows = this.nbFilterableRows + this.refRow;
} else {
if(this.isFirstLoad){
let fltrow;
if(!this.gridLayout){
let thead = Dom.tag(this.tbl, &apos;thead&apos;);
if(thead.length &gt; 0){
fltrow = thead[0].insertRow(this.filtersRowIndex);
} else {
fltrow = this.tbl.insertRow(this.filtersRowIndex);
}
if(this.headersRow &gt; 1 &amp;&amp;
this.filtersRowIndex &lt;= this.headersRow &amp;&amp;
!this.popUpFilters){
this.headersRow++;
}
if(this.popUpFilters){
this.headersRow++;
}
fltrow.className = this.fltsRowCssClass;
//Disable for grid_layout
if(this.isExternalFlt &amp;&amp;
(!this.gridLayout || this.popUpFilters)){
fltrow.style.display = &apos;none&apos;;
}
}
this.nbFilterableRows = this.getRowsNb();
this.nbVisibleRows = this.nbFilterableRows;
this.nbRows = this.tbl.rows.length;
for(let i=0; i&lt;n; i++){// this loop adds filters
if(this.popUpFilters){
Mod.popupFilter.build(i);
}
let fltcell = Dom.create(this.fltCellTag),
col = this.getFilterType(i),
externalFltTgtId =
this.isExternalFlt &amp;&amp; this.externalFltTgtIds ?
this.externalFltTgtIds[i] : null;
if(this.singleSearchFlt){
fltcell.colSpan = this.nbCells;
}
if(!this.gridLayout){
fltrow.appendChild(fltcell);
}
inpclass = (i==n-1 &amp;&amp; this.displayBtn) ?
this.fltSmallCssClass : this.fltCssClass;
//only 1 input for single search
if(this.singleSearchFlt){
col = this.fltTypeInp;
inpclass = this.singleFltCssClass;
}
//drop-down filters
if(col===this.fltTypeSlc || col===this.fltTypeMulti){
if(!Mod.dropdown){
Mod.dropdown = new Dropdown(this);
}
let dropdown = Mod.dropdown;
let slc = Dom.create(this.fltTypeSlc,
[&apos;id&apos;, this.prfxFlt+i+&apos;_&apos;+this.id],
[&apos;ct&apos;, i], [&apos;filled&apos;, &apos;0&apos;]
);
if(col===this.fltTypeMulti){
slc.multiple = this.fltTypeMulti;
slc.title = dropdown.multipleSlcTooltip;
}
slc.className = Str.lower(col)===this.fltTypeSlc ?
inpclass : this.fltMultiCssClass;// for ie&lt;=6
//filter is appended in desired external element
if(externalFltTgtId){
Dom.id(externalFltTgtId).appendChild(slc);
this.externalFltEls.push(slc);
} else {
fltcell.appendChild(slc);
}
this.fltIds.push(this.prfxFlt+i+&apos;_&apos;+this.id);
if(!this.fillSlcOnDemand){
dropdown._build(i);
}
Event.add(slc, &apos;keypress&apos;,
this.Evt.detectKey.bind(this));
Event.add(slc, &apos;change&apos;,
this.Evt.onSlcChange.bind(this));
Event.add(slc, &apos;focus&apos;, this.Evt.onSlcFocus.bind(this));
//1st option is created here since dropdown.build isn&apos;t
//invoked
if(this.fillSlcOnDemand){
let opt0 = Dom.createOpt(this.displayAllText, &apos;&apos;);
slc.appendChild(opt0);
}
}
// checklist
else if(col===this.fltTypeCheckList){
let checkList;
Mod.checkList = new CheckList(this);
checkList = Mod.checkList;
let divCont = Dom.create(&apos;div&apos;,
[&apos;id&apos;, checkList.prfxCheckListDiv+i+&apos;_&apos;+this.id],
[&apos;ct&apos;, i], [&apos;filled&apos;, &apos;0&apos;]);
divCont.className = checkList.checkListDivCssClass;
//filter is appended in desired element
if(externalFltTgtId){
Dom.id(externalFltTgtId).appendChild(divCont);
this.externalFltEls.push(divCont);
} else {
fltcell.appendChild(divCont);
}
checkList.checkListDiv[i] = divCont;
this.fltIds.push(this.prfxFlt+i+&apos;_&apos;+this.id);
if(!this.fillSlcOnDemand){
checkList._build(i);
}
if(this.fillSlcOnDemand){
Event.add(divCont, &apos;click&apos;,
this.Evt.onCheckListClick.bind(this));
divCont.appendChild(
Dom.text(checkList.activateCheckListTxt));
}
}
else{
//show/hide input
let inptype = col===this.fltTypeInp ? &apos;text&apos; : &apos;hidden&apos;;
let inp = Dom.create(this.fltTypeInp,
[&apos;id&apos;,this.prfxFlt+i+&apos;_&apos;+this.id],
[&apos;type&apos;,inptype], [&apos;ct&apos;,i]);
if(inptype!==&apos;hidden&apos; &amp;&amp; this.watermark){
inp.setAttribute(
&apos;placeholder&apos;,
this.isWatermarkArray ?
(this.watermark[i] || &apos;&apos;) : this.watermark
);
}
inp.className = inpclass;
Event.add(inp, &apos;focus&apos;, this.Evt.onInpFocus.bind(this));
//filter is appended in desired element
if(externalFltTgtId){
Dom.id(externalFltTgtId).appendChild(inp);
this.externalFltEls.push(inp);
} else {
fltcell.appendChild(inp);
}
this.fltIds.push(this.prfxFlt+i+&apos;_&apos;+this.id);
Event.add(inp, &apos;keypress&apos;,
this.Evt.detectKey.bind(this));
Event.add(inp, &apos;keydown&apos;,
this.Evt.onKeyDown.bind(this));
Event.add(inp, &apos;keyup&apos;, this.Evt.onKeyUp.bind(this));
Event.add(inp, &apos;blur&apos;, this.Evt.onInpBlur.bind(this));
if(this.rememberGridValues){
let flts_values = this.Mod.store.getFilterValues(
this.fltsValuesCookie);
if(flts_values[i]!=&apos; &apos;){
this.setFilterValue(i, flts_values[i], false);
}
}
}
// this adds submit button
if(i==n-1 &amp;&amp; this.displayBtn){
let btn = Dom.create(this.fltTypeInp,
[&apos;id&apos;,this.prfxValButton+i+&apos;_&apos;+this.id],
[&apos;type&apos;,&apos;button&apos;], [&apos;value&apos;,this.btnText]);
btn.className = this.btnCssClass;
//filter is appended in desired element
if(externalFltTgtId){
Dom.id(externalFltTgtId).appendChild(btn);
} else{
fltcell.appendChild(btn);
}
Event.add(btn, &apos;click&apos;, this.Evt.onBtnClick.bind(this));
}//if
}// for i
} else {
this._resetGrid();
}//if isFirstLoad
}//if this.fltGrid
/* Filter behaviours */
if(this.hasVisibleRows){
this.enforceVisibility();
}
if(this.rowsCounter){
Mod.rowsCounter = new RowsCounter(this);
Mod.rowsCounter.init();
}
if(this.statusBar){
Mod.statusBar = new StatusBar(this);
Mod.statusBar.init();
}
if(this.paging || Mod.paging){
if(!Mod.paging){
Mod.paging = new Paging(this);
}
// TODO: handle both cases in paging init
if(Mod.paging.isPagingRemoved){
Mod.paging.reset();
} else {
Mod.paging.init();
}
}
if(this.btnReset){
Mod.clearButton = new ClearButton(this);
Mod.clearButton.init();
}
if(this.helpInstructions){
if(!Mod.help){
Mod.help = new Help(this);
}
Mod.help.init();
}
if(this.hasColWidths &amp;&amp; !this.gridLayout){
this.setColWidths();
}
if(this.alternateBgs){
Mod.alternateRows = new AlternateRows(this);
Mod.alternateRows.init();
}
this.isFirstLoad = false;
this._hasGrid = true;
if(this.rememberGridValues || this.rememberPageLen ||
this.rememberPageNb){
this.resetValues();
}
//TF css class is added to table
if(!this.gridLayout){
Dom.addClass(this.tbl, this.prfxTf);
}
if(this.loader){
Mod.loader.show(&apos;none&apos;);
}
/* Loads extensions */
if(this.hasExtensions){
this.initExtensions();
}
if(this.onFiltersLoaded){
this.onFiltersLoaded.call(null, this);
}
}
/**
* Manages state messages
* @param {String} evt Event name
* @param {Object} cfg Config object
*/
EvtManager(evt,
cfg={ slcIndex: null, slcExternal: false, slcId: null, pgIndex: null }){
let slcIndex = cfg.slcIndex;
let slcExternal = cfg.slcExternal;
let slcId = cfg.slcId;
let pgIndex = cfg.pgIndex;
let cpt = this.Mod;
function efx(){
/*jshint validthis:true */
let ev = this.Evt.name;
switch(evt){
case ev.filter:
this._filter();
break;
case ev.dropdown:
if(this.linkedFilters){
cpt.dropdown._build(slcIndex, true);
} else {
cpt.dropdown._build(
slcIndex, false, slcExternal, slcId);
}
break;
case ev.checklist:
cpt.checkList._build(slcIndex, slcExternal, slcId);
break;
case ev.changepage:
cpt.paging._changePage(pgIndex);
break;
case ev.clear:
this._clearFilters();
this._filter();
break;
case ev.changeresultsperpage:
cpt.paging._changeResultsPerPage();
break;
case ev.resetvalues:
this._resetValues();
this._filter();
break;
case ev.resetpage:
cpt.paging._resetPage(this.pgNbCookie);
break;
case ev.resetpagelength:
cpt.paging._resetPageLength(this.pgLenCookie);
break;
case ev.loadextensions:
this._loadExtensions();
break;
case ev.loadthemes:
this._loadThemes();
break;
}
if(this.statusBar){
cpt.statusBar.message(&apos;&apos;);
}
if(this.loader){
cpt.loader.show(&apos;none&apos;);
}
}
if(!this.loader &amp;&amp; !this.statusBar &amp;&amp; !this.linkedFilters) {
efx.call(this);
} else {
if(this.loader){
cpt.loader.show(&apos;&apos;);
}
if(this.statusBar){
cpt.statusBar.message(this[&apos;msg&apos;+evt]);
}
global.setTimeout(efx.bind(this), this.execDelay);
}
}
/**
* Return a feature instance for a given name
* @param {String} name Name of the feature
* @return {Object}
*/
feature(name){
return this.Mod[name];
}
/**
* Initialise all the extensions defined in the configuration object
*/
initExtensions(){
let exts = this.extensions;
for(let i=0, len=exts.length; i&lt;len; i++){
let ext = exts[i];
if(!this.ExtRegistry[ext.name]){
this.loadExtension(ext);
}
}
}
/**
* Load an extension module
* @param {Object} ext Extension config object
*/
loadExtension(ext){
if(!ext || !ext.name){
return;
}
let name = ext.name;
let path = ext.path;
let modulePath;
if(name &amp;&amp; path){
modulePath = ext.path + name;
} else {
name = name.replace(&apos;.js&apos;, &apos;&apos;);
modulePath = &apos;extensions/{}/{}&apos;.replace(/{}/g, name);
}
require([&apos;./&apos; + modulePath], (mod)=&gt; {
let inst = new mod(this, ext);
inst.init();
this.ExtRegistry[name] = inst;
});
}
/**
* Get an extension instance
* @param {String} name Name of the extension
* @return {Object} Extension instance
*/
extension(name){
return this.ExtRegistry[name];
}
/**
* Check passed extension name exists
* @param {String} name Name of the extension
* @return {Boolean}
*/
hasExtension(name){
return !Types.isEmpty(this.ExtRegistry[name]);
}
/**
* Destroy all the extensions defined in the configuration object
*/
destroyExtensions(){
let exts = this.extensions;
for(let i=0, len=exts.length; i&lt;len; i++){
let ext = exts[i];
let extInstance = this.ExtRegistry[ext.name];
if(extInstance){
extInstance.destroy();
this.ExtRegistry[ext.name] = null;
}
}
}
loadThemes(){
this.EvtManager(this.Evt.name.loadthemes);
}
/**
* Load themes defined in the configuration object
*/
_loadThemes(){
let themes = this.themes;
//Default theme config
if(this.enableDefaultTheme){
let defaultTheme = { name: &apos;default&apos; };
this.themes.push(defaultTheme);
}
if(Types.isArray(themes)){
for(let i=0, len=themes.length; i&lt;len; i++){
let theme = themes[i];
let name = theme.name;
let path = theme.path;
let styleId = this.prfxTf + name;
if(name &amp;&amp; !path){
path = this.themesPath + name + &apos;/&apos; + name + &apos;.css&apos;;
}
else if(!name &amp;&amp; theme.path){
name = &apos;theme{0}&apos;.replace(&apos;{0}&apos;, i);
}
if(!this.isImported(path, &apos;link&apos;)){
this.import(styleId, path, null, &apos;link&apos;);
}
}
}
//Some elements need to be overriden for default theme
//Reset button
this.btnResetText = null;
this.btnResetHtml = &apos;&lt;input type=&quot;button&quot; value=&quot;&quot; class=&quot;&apos; +
this.btnResetCssClass+&apos;&quot; title=&quot;Clear filters&quot; /&gt;&apos;;
//Paging buttons
this.btnPrevPageHtml = &apos;&lt;input type=&quot;button&quot; value=&quot;&quot; class=&quot;&apos; +
this.btnPageCssClass+&apos; previousPage&quot; title=&quot;Previous page&quot; /&gt;&apos;;
this.btnNextPageHtml = &apos;&lt;input type=&quot;button&quot; value=&quot;&quot; class=&quot;&apos; +
this.btnPageCssClass+&apos; nextPage&quot; title=&quot;Next page&quot; /&gt;&apos;;
this.btnFirstPageHtml = &apos;&lt;input type=&quot;button&quot; value=&quot;&quot; class=&quot;&apos; +
this.btnPageCssClass+&apos; firstPage&quot; title=&quot;First page&quot; /&gt;&apos;;
this.btnLastPageHtml = &apos;&lt;input type=&quot;button&quot; value=&quot;&quot; class=&quot;&apos; +
this.btnPageCssClass+&apos; lastPage&quot; title=&quot;Last page&quot; /&gt;&apos;;
//Loader
this.loader = true;
this.loaderHtml = &apos;&lt;div class=&quot;defaultLoader&quot;&gt;&lt;/div&gt;&apos;;
this.loaderText = null;
}
/**
* Return stylesheet DOM element for a given theme name
* @return {DOMElement} stylesheet element
*/
getStylesheet(name=&apos;default&apos;){
return Dom.id(this.prfxTf + name);
}
/**
* Destroy filter grid
*/
destroy(){
if(!this._hasGrid){
return;
}
let rows = this.tbl.rows,
Mod = this.Mod;
if(this.isExternalFlt &amp;&amp; !this.popUpFilters){
this.removeExternalFlts();
}
if(this.infDiv){
this.removeToolbar();
}
if(this.highlightKeywords){
Mod.highlightKeyword.unhighlightAll();
}
if(this.markActiveColumns){
this.clearActiveColumns();
}
if(this.hasExtensions){
this.destroyExtensions();
}
//this loop shows all rows and removes validRow attribute
for(let j=this.refRow; j&lt;this.nbRows; j++){
rows[j].style.display = &apos;&apos;;
if(rows[j].hasAttribute(&apos;validRow&apos;)){
rows[j].removeAttribute(&apos;validRow&apos;);
}
//removes alternating colors
if(this.alternateBgs){
Mod.alternateRows.removeRowBg(j);
}
}//for j
if(this.fltGrid &amp;&amp; !this.gridLayout){
this.fltGridEl = rows[this.filtersRowIndex];
this.tbl.deleteRow(this.filtersRowIndex);
}
// Destroy modules
Object.keys(Mod).forEach(function(key) {
var feature = Mod[key];
if(feature &amp;&amp; Types.isFn(feature.destroy)){
feature.destroy();
}
});
Dom.removeClass(this.tbl, this.prfxTf);
this.activeFlt = null;
this.isStartBgAlternate = true;
this._hasGrid = false;
this.tbl = null;
}
/**
* Generate container element for paging, reset button, rows counter etc.
*/
setToolbar(){
if(this.infDiv){
return;
}
/*** container div ***/
let infdiv = Dom.create(&apos;div&apos;, [&apos;id&apos;, this.prfxInfDiv+this.id]);
infdiv.className = this.infDivCssClass;
//custom container
if(this.toolBarTgtId){
Dom.id(this.toolBarTgtId).appendChild(infdiv);
}
//grid-layout
else if(this.gridLayout){
let gridLayout = this.Mod.gridLayout;
gridLayout.tblMainCont.appendChild(infdiv);
infdiv.className = gridLayout.gridInfDivCssClass;
}
//default location: just above the table
else{
var cont = Dom.create(&apos;caption&apos;);
cont.appendChild(infdiv);
this.tbl.insertBefore(cont, this.tbl.firstChild);
}
this.infDiv = Dom.id(this.prfxInfDiv+this.id);
/*** left div containing rows # displayer ***/
let ldiv = Dom.create(&apos;div&apos;, [&apos;id&apos;, this.prfxLDiv+this.id]);
ldiv.className = this.lDivCssClass;
infdiv.appendChild(ldiv);
this.lDiv = Dom.id(this.prfxLDiv+this.id);
/*** right div containing reset button
+ nb results per page select ***/
let rdiv = Dom.create(&apos;div&apos;, [&apos;id&apos;, this.prfxRDiv+this.id]);
rdiv.className = this.rDivCssClass;
infdiv.appendChild(rdiv);
this.rDiv = Dom.id(this.prfxRDiv+this.id);
/*** mid div containing paging elements ***/
let mdiv = Dom.create(&apos;div&apos;, [&apos;id&apos;, this.prfxMDiv+this.id]);
mdiv.className = this.mDivCssClass;
infdiv.appendChild(mdiv);
this.mDiv = Dom.id(this.prfxMDiv+this.id);
// Enable help instructions by default if topbar is generated and not
// explicitely set to false
if(Types.isUndef(this.helpInstructions)){
if(!this.Mod.help){
this.Mod.help = new Help(this);
}
this.Mod.help.init();
this.helpInstructions = true;
}
}
/**
* Remove toolbar container element
*/
removeToolbar(){
if(!this.infDiv){
return;
}
this.infDiv.parentNode.removeChild(this.infDiv);
this.infDiv = null;
let tbl = this.tbl;
let captions = Dom.tag(tbl, &apos;caption&apos;);
if(captions.length &gt; 0){
[].forEach.call(captions, function(elm) {
tbl.removeChild(elm);
});
}
}
/**
* Remove all the external column filters
*/
removeExternalFlts(){
if(!this.isExternalFlt || !this.externalFltTgtIds){
return;
}
let ids = this.externalFltTgtIds,
len = ids.length;
for(let ct=0; ct&lt;len; ct++){
let externalFltTgtId = ids[ct],
externalFlt = Dom.id(externalFltTgtId);
if(externalFlt){
externalFlt.innerHTML = &apos;&apos;;
}
}
}
/**
* Check if given column implements a filter with custom options
* @param {Number} colIndex Column&apos;s index
* @return {Boolean}
*/
isCustomOptions(colIndex) {
return this.hasCustomOptions &amp;&amp;
this.customOptions.cols.indexOf(colIndex) != -1;
}
/**
* Returns an array [[value0, value1 ...],[text0, text1 ...]] with the
* custom options values and texts
* @param {Number} colIndex Column&apos;s index
* @return {Array}
*/
getCustomOptions(colIndex){
if(!colIndex || !this.isCustomOptions(colIndex)){
return;
}
let customOptions = this.customOptions;
let cols = customOptions.cols;
let optTxt = [], optArray = [];
let index = Arr.indexByValue(cols, colIndex);
let slcValues = customOptions.values[index];
let slcTexts = customOptions.texts[index];
let slcSort = customOptions.sorts[index];
for(let r=0, len=slcValues.length; r&lt;len; r++){
optArray.push(slcValues[r]);
if(slcTexts[r]){
optTxt.push(slcTexts[r]);
} else {
optTxt.push(slcValues[r]);
}
}
if(slcSort){
optArray.sort();
optTxt.sort();
}
return [optArray, optTxt];
}
resetValues(){
this.EvtManager(this.Evt.name.resetvalues);
}
/**
* Reset persisted filter values
*/
_resetValues(){
//only fillSlcOnDemand
if(this.rememberGridValues &amp;&amp; this.fillSlcOnDemand){
this._resetGridValues(this.fltsValuesCookie);
}
if(this.rememberPageLen &amp;&amp; this.Mod.paging){
this.Mod.paging.resetPageLength(this.pgLenCookie);
}
if(this.rememberPageNb &amp;&amp; this.Mod.paging){
this.Mod.paging.resetPage(this.pgNbCookie);
}
}
/**
* Reset persisted filter values when load filters on demand feature is
* enabled
* @param {String} name cookie name storing filter values
*/
_resetGridValues(name){
if(!this.fillSlcOnDemand){
return;
}
let fltsValues = this.Mod.store.getFilterValues(name),
slcFltsIndex = this.getFiltersByType(this.fltTypeSlc, true),
multiFltsIndex = this.getFiltersByType(this.fltTypeMulti, true);
//if the number of columns is the same as before page reload
if(Number(fltsValues[(fltsValues.length-1)]) === this.fltIds.length){
for(let i=0; i&lt;(fltsValues.length - 1); i++){
if(fltsValues[i]===&apos; &apos;){
continue;
}
let s, opt;
let fltType = this.getFilterType(i);
// if fillSlcOnDemand, drop-down needs to contain stored
// value(s) for filtering
if(fltType===this.fltTypeSlc || fltType===this.fltTypeMulti){
let slc = Dom.id( this.fltIds[i] );
slc.options[0].selected = false;
//selects
if(Arr.has(slcFltsIndex, i)){
opt = Dom.createOpt(fltsValues[i],fltsValues[i],true);
slc.appendChild(opt);
this.hasStoredValues = true;
}
//multiple select
if(Arr.has(multiFltsIndex, i)){
s = fltsValues[i].split(&apos; &apos;+this.orOperator+&apos; &apos;);
for(let j=0, len=s.length; j&lt;len; j++){
if(s[j]===&apos;&apos;){
continue;
}
opt = Dom.createOpt(s[j],s[j],true);
slc.appendChild(opt);
this.hasStoredValues = true;
}
}// if multiFltsIndex
}
else if(fltType===this.fltTypeCheckList){
let checkList = this.Mod.checkList;
let divChk = checkList.checkListDiv[i];
divChk.title = divChk.innerHTML;
divChk.innerHTML = &apos;&apos;;
let ul = Dom.create(
&apos;ul&apos;,[&apos;id&apos;,this.fltIds[i]],[&apos;colIndex&apos;,i]);
ul.className = checkList.checkListCssClass;
let li0 = Dom.createCheckItem(
this.fltIds[i]+&apos;_0&apos;, &apos;&apos;, this.displayAllText);
li0.className = checkList.checkListItemCssClass;
ul.appendChild(li0);
divChk.appendChild(ul);
s = fltsValues[i].split(&apos; &apos;+this.orOperator+&apos; &apos;);
for(let j=0, len=s.length; j&lt;len; j++){
if(s[j]===&apos;&apos;){
continue;
}
let li = Dom.createCheckItem(
this.fltIds[i]+&apos;_&apos;+(j+1), s[j], s[j]);
li.className = checkList.checkListItemCssClass;
ul.appendChild(li);
li.check.checked = true;
checkList.setCheckListValues(li.check);
this.hasStoredValues = true;
}
}
}//end for
if(!this.hasStoredValues &amp;&amp; this.paging){
this.Mod.paging.setPagingInfo();
}
}//end if
}
filter(){
this.EvtManager(this.Evt.name.filter);
}
/**
* 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
* hidden when all the search terms are not found in inspected row.
*
* TODO: Reduce complexity of this massive method
*/
_filter(){
if(!this.fltGrid || (!this._hasGrid &amp;&amp; !this.isFirstLoad)){
return;
}
//invoke onbefore callback
if(this.onBeforeFilter){
this.onBeforeFilter.call(null, this);
}
let row = this.tbl.rows,
Mod = this.Mod,
hiddenrows = 0;
this.validRowsIndex = [];
// removes keyword highlighting
if(this.highlightKeywords){
Mod.highlightKeyword.unhighlightAll();
}
//removes popup filters active icons
if(this.popUpFilters){
Mod.popupFilter.buildIcons();
}
//removes active column header class
if(this.markActiveColumns){
this.clearActiveColumns();
}
// search args re-init
this.searchArgs = this.getFiltersValue();
var num_cell_data, nbFormat;
var re_le = new RegExp(this.leOperator),
re_ge = new RegExp(this.geOperator),
re_l = new RegExp(this.lwOperator),
re_g = new RegExp(this.grOperator),
re_d = new RegExp(this.dfOperator),
re_lk = new RegExp(Str.rgxEsc(this.lkOperator)),
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),
re_em = this.emOperator,
re_nm = this.nmOperator,
re_re = new RegExp(Str.rgxEsc(this.rgxOperator));
//keyword highlighting
function highlight(str, ok, cell){
/*jshint validthis:true */
if(this.highlightKeywords &amp;&amp; ok){
str = str.replace(re_lk, &apos;&apos;);
str = str.replace(re_eq, &apos;&apos;);
str = str.replace(re_st, &apos;&apos;);
str = str.replace(re_en, &apos;&apos;);
let w = str;
if(re_le.test(str) || re_ge.test(str) || re_l.test(str) ||
re_g.test(str) || re_d.test(str)){
w = Dom.getText(cell);
}
if(w !== &apos;&apos;){
Mod.highlightKeyword.highlight(
cell, w, Mod.highlightKeyword.highlightCssClass);
}
}
}
//looks for search argument in current row
function hasArg(sA, cell_data, j){
/*jshint validthis:true */
let occurence,
removeNbFormat = Helpers.removeNbFormat;
//Search arg operator tests
let hasLO = re_l.test(sA),
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),
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
let isLDate = hasLO &amp;&amp; isValidDate(sA.replace(re_l,&apos;&apos;), dtType);
let isLEDate = hasLE &amp;&amp; isValidDate(sA.replace(re_le,&apos;&apos;), dtType);
let isGDate = hasGR &amp;&amp; isValidDate(sA.replace(re_g,&apos;&apos;), dtType);
let isGEDate = hasGE &amp;&amp; isValidDate(sA.replace(re_ge,&apos;&apos;), dtType);
let isDFDate = hasDF &amp;&amp; isValidDate(sA.replace(re_d,&apos;&apos;), dtType);
let isEQDate = hasEQ &amp;&amp; isValidDate(sA.replace(re_eq,&apos;&apos;), dtType);
let dte1, dte2;
//dates
if(isValidDate(cell_data,dtType)){
dte1 = formatDate(cell_data,dtType);
// lower date
if(isLDate){
dte2 = formatDate(sA.replace(re_l,&apos;&apos;), dtType);
occurence = dte1 &lt; dte2;
}
// lower equal date
else if(isLEDate){
dte2 = formatDate(sA.replace(re_le,&apos;&apos;), dtType);
occurence = dte1 &lt;= dte2;
}
// greater equal date
else if(isGEDate){
dte2 = formatDate(sA.replace(re_ge,&apos;&apos;), dtType);
occurence = dte1 &gt;= dte2;
}
// greater date
else if(isGDate){
dte2 = formatDate(sA.replace(re_g,&apos;&apos;), dtType);
occurence = dte1 &gt; dte2;
}
// different date
else if(isDFDate){
dte2 = formatDate(sA.replace(re_d,&apos;&apos;), dtType);
occurence = dte1.toString() != dte2.toString();
}
// equal date
else if(isEQDate){
dte2 = formatDate(sA.replace(re_eq,&apos;&apos;), dtType);
occurence = dte1.toString() == dte2.toString();
}
// searched keyword with * operator doesn&apos;t have to be a date
else if(re_lk.test(sA)){// like date
occurence = this._containsStr(
sA.replace(re_lk,&apos;&apos;), cell_data, null, false);
}
else if(isValidDate(sA,dtType)){
dte2 = formatDate(sA,dtType);
occurence = dte1.toString() == dte2.toString();
}
//empty
else if(hasEM){
occurence = Str.isEmpty(cell_data);
}
//non-empty
else if(hasNM){
occurence = !Str.isEmpty(cell_data);
}
}
else{
//first numbers need to be formated
if(this.hasColNbFormat &amp;&amp; this.colNbFormat[j]){
num_cell_data = removeNbFormat(
cell_data, this.colNbFormat[j]);
nbFormat = this.colNbFormat[j];
} else {
if(this.thousandsSeparator === &apos;,&apos; &amp;&amp;
this.decimalSeparator === &apos;.&apos;){
num_cell_data = removeNbFormat(cell_data, &apos;us&apos;);
nbFormat = &apos;us&apos;;
} else {
num_cell_data = removeNbFormat(cell_data, &apos;eu&apos;);
nbFormat = &apos;eu&apos;;
}
}
// first checks if there is any operator (&lt;,&gt;,&lt;=,&gt;=,!,*,=,{,},
// rgx:)
// lower equal
if(hasLE){
occurence = num_cell_data &lt;= removeNbFormat(
sA.replace(re_le, &apos;&apos;), nbFormat);
}
//greater equal
else if(hasGE){
occurence = num_cell_data &gt;= removeNbFormat(
sA.replace(re_ge, &apos;&apos;), nbFormat);
}
//lower
else if(hasLO){
occurence = num_cell_data &lt; removeNbFormat(
sA.replace(re_l, &apos;&apos;), nbFormat);
}
//greater
else if(hasGR){
occurence = num_cell_data &gt; removeNbFormat(
sA.replace(re_g, &apos;&apos;), nbFormat);
}
//different
else if(hasDF){
occurence = this._containsStr(
sA.replace(re_d, &apos;&apos;),cell_data) ? false : true;
}
//like
else if(hasLK){
occurence = this._containsStr(
sA.replace(re_lk, &apos;&apos;), cell_data, null, false);
}
//equal
else if(hasEQ){
occurence = this._containsStr(
sA.replace(re_eq, &apos;&apos;), cell_data, null, true);
}
//starts with
else if(hasST){
occurence = cell_data.indexOf(sA.replace(re_st, &apos;&apos;))===0 ?
true : false;
}
//ends with
else if(hasEN){
let searchArg = sA.replace(re_en, &apos;&apos;);
occurence =
cell_data.lastIndexOf(searchArg,cell_data.length-1) ===
(cell_data.length-1)-(searchArg.length-1) &amp;&amp;
cell_data.lastIndexOf(
searchArg, cell_data.length-1) &gt; -1 ? true : false;
}
//empty
else if(hasEM){
occurence = Str.isEmpty(cell_data);
}
//non-empty
else if(hasNM){
occurence = !Str.isEmpty(cell_data);
}
//regexp
else if(hasRE){
//in case regexp fires an exception
try{
//operator is removed
let srchArg = sA.replace(re_re,&apos;&apos;);
let rgx = new RegExp(srchArg);
occurence = rgx.test(cell_data);
} catch(e) { occurence = false; }
}
else{
occurence = this._containsStr(
sA, cell_data, this.getFilterType(j));
}
}//else
return occurence;
}//fn
for(let k=this.refRow; k&lt;this.nbRows; k++){
/*** if table already filtered some rows are not visible ***/
if(row[k].style.display === &apos;none&apos;){
row[k].style.display = &apos;&apos;;
}
let cell = row[k].cells,
nchilds = cell.length;
// checks if row has exact cell #
if(nchilds !== this.nbCells){
continue;
}
let occurence = [],
isRowValid = true,
//only for single filter search
singleFltRowValid = false;
// this loop retrieves cell data
for(let j=0; j&lt;nchilds; j++){
//searched keyword
let sA = this.searchArgs[this.singleSearchFlt ? 0 : j];
var dtType = this.hasColDateType ?
this.colDateType[j] : this.defaultDateType;
if(sA === &apos;&apos;){
continue;
}
let cell_data = Str.matchCase(
this.getCellData(j, cell[j]), this.caseSensitive);
//multiple search parameter operator ||
let sAOrSplit = sA.split(this.orOperator),
//multiple search || parameter boolean
hasMultiOrSA = (sAOrSplit.length&gt;1) ? true : false,
//multiple search parameter operator &amp;&amp;
sAAndSplit = sA.split(this.anOperator),
//multiple search &amp;&amp; parameter boolean
hasMultiAndSA = sAAndSplit.length&gt;1 ? true : false;
//multiple sarch parameters
if(hasMultiOrSA || hasMultiAndSA){
let cS,
occur = false,
s = hasMultiOrSA ? sAOrSplit : sAAndSplit;
for(let w=0, len=s.length; w&lt;len; w++){
cS = Str.trim(s[w]);
occur = hasArg.call(this, cS, cell_data, j);
highlight.call(this, cS, occur, cell[j]);
if(hasMultiOrSA &amp;&amp; occur){
break;
}
if(hasMultiAndSA &amp;&amp; !occur){
break;
}
}
occurence[j] = occur;
}
//single search parameter
else {
occurence[j] =
hasArg.call(this, Str.trim(sA), cell_data, j);
highlight.call(this, sA, occurence[j], cell[j]);
}//else single param
if(!occurence[j]){
isRowValid = false;
}
if(this.singleSearchFlt &amp;&amp; occurence[j]){
singleFltRowValid = true;
}
if(this.popUpFilters){
Mod.popupFilter.buildIcon(j, true);
}
if(this.markActiveColumns){
if(k === this.refRow){
if(this.onBeforeActiveColumn){
this.onBeforeActiveColumn.call(null, this, j);
}
Dom.addClass(
this.getHeaderElement(j),
this.activeColumnsCssClass);
if(this.onAfterActiveColumn){
this.onAfterActiveColumn.call(null, this, j);
}
}
}
}//for j
if(this.singleSearchFlt &amp;&amp; singleFltRowValid){
isRowValid = true;
}
if(!isRowValid){
this.validateRow(k, false);
if(Mod.alternateRows){
Mod.alternateRows.removeRowBg(k);
}
// always visible rows need to be counted as valid
if(this.hasVisibleRows &amp;&amp; this.visibleRows.indexOf(k) !== -1){
this.validRowsIndex.push(k);
} else {
hiddenrows++;
}
} else {
this.validateRow(k, true);
this.validRowsIndex.push(k);
if(this.alternateBgs){
Mod.alternateRows.setRowBg(k, this.validRowsIndex.length);
}
if(this.onRowValidated){
this.onRowValidated.call(null, this, k);
}
}
}// for k
this.nbVisibleRows = this.validRowsIndex.length;
this.nbHiddenRows = hiddenrows;
if(this.rememberGridValues){
Mod.store.saveFilterValues(this.fltsValuesCookie);
}
//applies filter props after filtering process
if(!this.paging){
this.applyProps();
} else {
// Shouldn&apos;t need to care of that here...
// TODO: provide a method in paging module
Mod.paging.startPagingRow = 0;
Mod.paging.currentPageNb = 1;
//
Mod.paging.setPagingInfo(this.validRowsIndex);
}
//invokes onafter callback
if(this.onAfterFilter){
this.onAfterFilter.call(null,this);
}
}
/**
* Re-apply the features/behaviour concerned by filtering/paging operation
*
* NOTE: this will disappear whenever custom events in place
*/
applyProps(){
let Mod = this.Mod;
//shows rows always visible
if(this.hasVisibleRows){
this.enforceVisibility();
}
//columns operations
if(this.hasExtension(&apos;colOps&apos;)){
this.extension(&apos;colOps&apos;).calc();
}
//re-populates drop-down filters
if(this.linkedFilters){
this.linkFilters();
}
if(this.rowsCounter){
Mod.rowsCounter.refresh(this.nbVisibleRows);
}
if(this.popUpFilters){
Mod.popupFilter.closeAll();
}
}
/**
* Return the data of a specified colum
* @param {Number} colindex Column index
* @param {Boolean} num Return unformatted number
* @param {Array} exclude List of row indexes to be excluded
* @return {Array} Flat list of data for a column
*/
getColValues(colindex, num=false, exclude=undefined){
if(!this.fltGrid){
return;
}
let row = this.tbl.rows,
colValues = [];
for(let i=this.refRow; i&lt;this.nbRows; i++){
let isExludedRow = false;
// checks if current row index appears in exclude array
if(exclude &amp;&amp; Types.isArray(exclude)){
isExludedRow = Arr.has(exclude, i);
}
let cell = row[i].cells,
nchilds = cell.length;
// checks if row has exact cell # and is not excluded
if(nchilds === this.nbCells &amp;&amp; !isExludedRow){
// this loop retrieves cell data
for(let j=0; j&lt;nchilds; j++){
if(j != colindex || row[i].style.display !== &apos;&apos;){
continue;
}
let cell_data = Str.lower(this.getCellData(j, cell[j])),
nbFormat = this.colNbFormat ?
this.colNbFormat[colindex] : null,
data = num ?
Helpers.removeNbFormat(cell_data, nbFormat) :
cell_data;
colValues.push(data);
}
}
}
return colValues;
}
/**
* Return the filter&apos;s value of a specified column
* @param {Number} index Column index
* @return {String} Filter value
*/
getFilterValue(index){
if(!this.fltGrid){
return;
}
let fltValue,
flt = this.getFilterElement(index);
if(!flt){
return &apos;&apos;;
}
let fltColType = this.getFilterType(index);
if(fltColType !== this.fltTypeMulti &amp;&amp;
fltColType !== this.fltTypeCheckList){
fltValue = flt.value;
}
//mutiple select
else if(fltColType === this.fltTypeMulti){
fltValue = &apos;&apos;;
for(let j=0, len=flt.options.length; j&lt;len; j++){
if(flt.options[j].selected){
fltValue = fltValue.concat(
flt.options[j].value+&apos; &apos; +
this.orOperator + &apos; &apos;
);
}
}
//removes last operator ||
fltValue = fltValue.substr(0, fltValue.length-4);
}
//checklist
else if(fltColType === this.fltTypeCheckList){
if(flt.getAttribute(&apos;value&apos;) !== null){
fltValue = flt.getAttribute(&apos;value&apos;);
//removes last operator ||
fltValue = fltValue.substr(0, fltValue.length-3);
} else{
fltValue = &apos;&apos;;
}
}
return fltValue;
}
/**
* Return the filters&apos; values
* @return {Array} List of filters&apos; values
*/
getFiltersValue(){
if(!this.fltGrid){
return;
}
let searchArgs = [];
for(let i=0, len=this.fltIds.length; i&lt;len; i++){
searchArgs.push(
Str.trim(
Str.matchCase(this.getFilterValue(i), this.caseSensitive))
);
}
return searchArgs;
}
/**
* Return the ID of the filter of a specified column
* @param {Number} index Column&apos;s index
* @return {String} ID of the filter element
*/
getFilterId(index){
if(!this.fltGrid){
return;
}
return this.fltIds[index];
}
/**
* Return the list of ids of filters matching a specified type.
* Note: hidden filters are also returned
*
* @param {String} type Filter type string (&apos;input&apos;, &apos;select&apos;, &apos;multiple&apos;,
* &apos;checklist&apos;)
* @param {Boolean} bool If true returns columns indexes instead of IDs
* @return {[type]} List of element IDs or column indexes
*/
getFiltersByType(type, bool){
if(!this.fltGrid){
return;
}
let arr = [];
for(let i=0, len=this.fltIds.length; i&lt;len; i++){
let fltType = this.getFilterType(i);
if(fltType === Str.lower(type)){
let a = bool ? i : this.fltIds[i];
arr.push(a);
}
}
return arr;
}
/**
* Return the filter&apos;s DOM element for a given column
* @param {Number} index Column&apos;s index
* @return {DOMElement}
*/
getFilterElement(index){
let fltId = this.fltIds[index];
return Dom.id(fltId);
}
/**
* Return the number of cells for a given row index
* @param {Number} rowIndex Index of the row
* @return {Number} Number of cells
*/
getCellsNb(rowIndex=0){
let tr = this.tbl.rows[rowIndex];
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
*/
getRowsNb(includeHeaders){
let s = Types.isUndef(this.refRow) ? 0 : this.refRow,
ntrs = this.tbl.rows.length;
if(includeHeaders){ s = 0; }
return parseInt(ntrs-s, 10);
}
/**
* Return the data of a given cell
* @param {Number} i Column&apos;s index
* @param {Object} cell Cell&apos;s DOM object
* @return {String}
*/
getCellData(i, cell){
if(Types.isUndef(i) || !cell){
return &apos;&apos;;
}
//First checks for customCellData event
if(this.customCellData &amp;&amp; Arr.has(this.customCellDataCols, i)){
return this.customCellData.call(null, this, cell, i);
} else {
return Dom.getText(cell);
}
}
/**
* Return the table data with following format:
* [
* [rowIndex, [value0, value1...]],
* [rowIndex, [value0, value1...]]
* ]
* @return {Array}
*
* TODO: provide an API returning data in JSON format
*/
getTableData(){
let row = this.tbl.rows;
for(let k=this.refRow; k&lt;this.nbRows; k++){
let rowData = [k,[]];
let cells = row[k].cells;
// this loop retrieves cell data
for(let j=0, len=cells.length; j&lt;len; j++){
let cell_data = this.getCellData(j, cells[j]);
rowData[1].push(cell_data);
}
this.tblData.push(rowData);
}
return this.tblData;
}
/**
* Return the filtered data with following format:
* [
* [rowIndex, [value0, value1...]],
* [rowIndex, [value0, value1...]]
* ]
* @param {Boolean} includeHeaders Include headers row
* @return {Array}
*
* TODO: provide an API returning data in JSON format
*/
getFilteredData(includeHeaders){
if(!this.validRowsIndex){
return [];
}
let row = this.tbl.rows,
filteredData = [];
if(includeHeaders){
let table = this.gridLayout ?
this.Mod.gridLayout.headTbl : this.tbl,
r = table.rows[this.headersRow],
rowData = [r.rowIndex, []];
for(let j=0; j&lt;this.nbCells; j++){
let headerText = this.getCellData(j, r.cells[j]);
rowData[1].push(headerText);
}
filteredData.push(rowData);
}
let validRows = this.getValidRows(true);
for(let i=0; i&lt;validRows.length; i++){
let rData = [this.validRowsIndex[i],[]],
cells = row[this.validRowsIndex[i]].cells;
for(let k=0; k&lt;cells.length; k++){
let cell_data = this.getCellData(k, cells[k]);
rData[1].push(cell_data);
}
filteredData.push(rData);
}
return filteredData;
}
/**
* Return the filtered data for a given column index
* @param {Number} colIndex Colmun&apos;s index
* @return {Array} Flat list of values [&apos;val0&apos;,&apos;val1&apos;,&apos;val2&apos;...]
*
* TODO: provide an API returning data in JSON format
*/
getFilteredDataCol(colIndex){
if(colIndex===undefined){
return [];
}
let data = this.getFilteredData(),
colData = [];
for(let i=0, len=data.length; i&lt;len; i++){
let r = data[i],
//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
* @param {RowElement} DOM element of the row
* @return {String} Usually &apos;none&apos; or &apos;&apos;
*/
getRowDisplay(row){
if(!this.fltGrid || !Types.isObj(row)){
return;
}
return row.style.display;
}
/**
* Validate/invalidate row by setting the &apos;validRow&apos; attribute on the row
* @param {Number} rowIndex Index of the row
* @param {Boolean} isValid
*/
validateRow(rowIndex, isValid){
let row = this.tbl.rows[rowIndex];
if(!row || typeof isValid !== &apos;boolean&apos;){
return;
}
// always visible rows are valid
if(this.hasVisibleRows &amp;&amp; this.visibleRows.indexOf(rowIndex) !== -1){
isValid = true;
}
let displayFlag = isValid ? &apos;&apos; : &apos;none&apos;,
validFlag = isValid ? &apos;true&apos; : &apos;false&apos;;
row.style.display = displayFlag;
if(this.paging){
row.setAttribute(&apos;validRow&apos;, validFlag);
}
}
/**
* Validate all filterable rows
*/
validateAllRows(){
if(!this._hasGrid){
return;
}
this.validRowsIndex = [];
for(let k=this.refRow; k&lt;this.nbFilterableRows; k++){
this.validateRow(k, true);
this.validRowsIndex.push(k);
}
}
/**
* Set search value to a given filter
* @param {Number} index Column&apos;s index
* @param {String} searcharg Search term
*/
setFilterValue(index, searcharg=&apos;&apos;){
if((!this.fltGrid &amp;&amp; !this.isFirstLoad) ||
!this.getFilterElement(index)){
return;
}
let slc = this.getFilterElement(index),
fltColType = this.getFilterType(index);
if(fltColType !== this.fltTypeMulti &amp;&amp;
fltColType != this.fltTypeCheckList){
slc.value = searcharg;
}
//multiple selects
else if(fltColType === this.fltTypeMulti){
let s = searcharg.split(&apos; &apos;+this.orOperator+&apos; &apos;);
// let ct = 0; //keywords counter
for(let j=0, len=slc.options.length; j&lt;len; j++){
let option = slc.options[j];
if(s===&apos;&apos; || s[0]===&apos;&apos;){
option.selected = false;
}
if(option.value===&apos;&apos;){
option.selected = false;
}
if(option.value!==&apos;&apos; &amp;&amp;
Arr.has(s, option.value, true)){
option.selected = true;
}//if
}//for j
}
//checklist
else if(fltColType === this.fltTypeCheckList){
searcharg = Str.matchCase(searcharg, this.caseSensitive);
let sarg = searcharg.split(&apos; &apos;+this.orOperator+&apos; &apos;);
let lisNb = Dom.tag(slc,&apos;li&apos;).length;
slc.setAttribute(&apos;value&apos;, &apos;&apos;);
slc.setAttribute(&apos;indexes&apos;, &apos;&apos;);
for(let k=0; k&lt;lisNb; k++){
let li = Dom.tag(slc,&apos;li&apos;)[k],
lbl = Dom.tag(li,&apos;label&apos;)[0],
chk = Dom.tag(li,&apos;input&apos;)[0],
lblTxt = Str.matchCase(
Dom.getText(lbl), this.caseSensitive);
if(lblTxt !== &apos;&apos; &amp;&amp; Arr.has(sarg, lblTxt, true)){
chk.checked = true;
this.Mod.checkList.setCheckListValues(chk);
}
else{
chk.checked = false;
this.Mod.checkList.setCheckListValues(chk);
}
}
}
}
/**
* Set them columns&apos; widths as per configuration
* @param {Number} rowIndex Optional row index to apply the widths to
* @param {Element} tbl DOM element
*/
setColWidths(rowIndex, tbl){
if(!this.fltGrid || !this.hasColWidths){
return;
}
tbl = tbl || this.tbl;
let rIndex;
if(rowIndex===undefined){
rIndex = tbl.rows[0].style.display!=&apos;none&apos; ? 0 : 1;
} else{
rIndex = rowIndex;
}
setWidths.call(this, tbl.rows[rIndex]);
function setWidths(row){
/*jshint validthis:true */
let nbCols = this.nbCells;
let colWidths = this.colWidths;
if((nbCols != colWidths.length) || (nbCols != row.cells.length)){
throw new Error(&apos;Columns number mismatch!&apos;);
}
let colTags = Dom.tag(tbl, &apos;col&apos;);
let tblHasColTag = colTags.length &gt; 0;
let frag = !tblHasColTag ? doc.createDocumentFragment() : null;
for(let k=0; k&lt;nbCols; k++){
// row.cells[k].style.width = colWidths[k];
let col;
if(tblHasColTag){
col = colTags[k];
} else {
col = Dom.create(&apos;col&apos;, [&apos;id&apos;, this.id+&apos;_col_&apos;+k]);
frag.appendChild(col);
}
col.style.width = colWidths[k];
}
if(!tblHasColTag){
tbl.insertBefore(frag, tbl.firstChild);
}
}
}
/**
* Makes defined rows always visible
*/
enforceVisibility(){
if(!this.hasVisibleRows){
return;
}
for(let i=0, len=this.visibleRows.length; i&lt;len; i++){
let row = this.visibleRows[i];
//row index cannot be &gt; nrows
if(row &lt;= this.nbRows){
this.validateRow(row, true);
}
}
}
clearFilters(){
this.EvtManager(this.Evt.name.clear);
}
/**
* Clear all the filters&apos; values
*/
_clearFilters(){
if(!this.fltGrid){
return;
}
if(this.onBeforeReset){
this.onBeforeReset.call(null, this, this.getFiltersValue());
}
for(let i=0, len=this.fltIds.length; i&lt;len; i++){
this.setFilterValue(i, &apos;&apos;);
}
if(this.linkedFilters){
this.activeFilterId = &apos;&apos;;
this.linkFilters();
}
if(this.rememberPageLen){ Cookie.remove(this.pgLenCookie); }
if(this.rememberPageNb){ Cookie.remove(this.pgNbCookie); }
if(this.onAfterReset){ this.onAfterReset.call(null, this); }
}
/**
* Clears filtered columns visual indicator (background color)
* @return {[type]} [description]
*/
clearActiveColumns(){
for(let i=0, len=this.fltIds.length; i&lt;len; i++){
Dom.removeClass(
this.getHeaderElement(i), this.activeColumnsCssClass);
}
}
/**
* Refresh the filters subject to linking (&apos;select&apos;, &apos;multiple&apos;,
* &apos;checklist&apos; type)
*/
linkFilters(){
if(!this.activeFilterId){
return;
}
let slcA1 = this.getFiltersByType(this.fltTypeSlc, true),
slcA2 = this.getFiltersByType(this.fltTypeMulti, true),
slcA3 = this.getFiltersByType(this.fltTypeCheckList, true),
slcIndex = slcA1.concat(slcA2);
slcIndex = slcIndex.concat(slcA3);
let activeFlt = this.activeFilterId.split(&apos;_&apos;)[0];
activeFlt = activeFlt.split(this.prfxFlt)[1];
let slcSelectedValue;
for(let i=0, len=slcIndex.length; i&lt;len; i++){
let curSlc = Dom.id(this.fltIds[slcIndex[i]]);
slcSelectedValue = this.getFilterValue(slcIndex[i]);
// Welcome to cyclomatic complexity hell :)
// TODO: simplify/refactor if statement
if(activeFlt!==slcIndex[i] ||
(this.paging &amp;&amp; Arr.has(slcA1, slcIndex[i]) &amp;&amp;
activeFlt === slcIndex[i] ) ||
(!this.paging &amp;&amp; (Arr.has(slcA3, slcIndex[i]) ||
Arr.has(slcA2, slcIndex[i]))) ||
slcSelectedValue === this.displayAllText ){
if(Arr.has(slcA3, slcIndex[i])){
this.Mod.checkList.checkListDiv[slcIndex[i]].innerHTML = &apos;&apos;;
} else {
curSlc.innerHTML = &apos;&apos;;
}
//1st option needs to be inserted
if(this.fillSlcOnDemand) {
let opt0 = Dom.createOpt(this.displayAllText, &apos;&apos;);
if(curSlc){
curSlc.appendChild(opt0);
}
}
if(Arr.has(slcA3, slcIndex[i])){
this.Mod.checkList._build(slcIndex[i]);
} else {
this.Mod.dropdown._build(slcIndex[i], true);
}
this.setFilterValue(slcIndex[i], slcSelectedValue);
}
}// for i
}
/**
* Re-generate the filters grid bar when previously removed
*/
_resetGrid(){
if(this.isFirstLoad){
return;
}
let Mod = this.Mod;
let tbl = this.tbl;
let rows = tbl.rows;
let filtersRowIndex = this.filtersRowIndex;
let filtersRow = rows[filtersRowIndex];
// grid was removed, grid row element is stored in fltGridEl property
if(!this.gridLayout){
// If table has a thead ensure the filters row is appended in the
// thead element
if(tbl.tHead){
var tempRow = tbl.tHead.insertRow(this.filtersRowIndex);
tbl.tHead.replaceChild(this.fltGridEl, tempRow);
} else {
filtersRow.parentNode.insertBefore(this.fltGridEl, filtersRow);
}
}
// filters are appended in external placeholders elements
if(this.isExternalFlt){
let externalFltTgtIds = this.externalFltTgtIds;
for(let ct=0, len=externalFltTgtIds.length; ct&lt;len; ct++){
let extFlt = Dom.id(externalFltTgtIds[ct]);
if(!extFlt){ continue; }
let externalFltEl = this.externalFltEls[ct];
extFlt.appendChild(externalFltEl);
let colFltType = this.getFilterType(ct);
//IE special treatment for gridLayout, appended filters are
//empty
if(this.gridLayout &amp;&amp;
externalFltEl.innerHTML === &apos;&apos; &amp;&amp;
colFltType !== this.fltTypeInp){
if(colFltType === this.fltTypeSlc ||
colFltType === this.fltTypeMulti){
Mod.dropdown.build(ct);
}
if(colFltType === this.fltTypeCheckList){
Mod.checkList.build(ct);
}
}
}
}
this.nbFilterableRows = this.getRowsNb();
this.nbVisibleRows = this.nbFilterableRows;
this.nbRows = rows.length;
if(this.popUpFilters){
this.headersRow++;
Mod.popupFilter.buildAll();
}
if(!this.gridLayout){
Dom.addClass(this.tbl, this.prfxTf);
}
this._hasGrid = true;
}
/**
* Checks if passed data contains the searched arg
* @param {String} arg Search term
* @param {String} data Data string
* @param {String} fltType Filter type (&apos;input&apos;, &apos;select&apos;)
* @param {Boolean} forceMatch Exact match
* @return {Boolean]}
*
* TODO: move into string module, remove fltType in order to decouple it
* from TableFilter module
*/
_containsStr(arg, data, fltType, forceMatch){
// Improved by Cedric Wartel (cwl)
// automatic exact match for selects and special characters are now
// filtered
let regexp,
modifier = (this.caseSensitive) ? &apos;g&apos; : &apos;gi&apos;,
exactMatch = !forceMatch ? this.exactMatch : forceMatch;
if(exactMatch || (fltType!==this.fltTypeInp &amp;&amp; fltType)){
regexp = new RegExp(
&apos;(^\\s*)&apos;+ Str.rgxEsc(arg) +&apos;(\\s*$)&apos;, modifier);
} else{
regexp = new RegExp(Str.rgxEsc(arg), modifier);
}
return regexp.test(data);
}
/**
* Check if passed script or stylesheet is already imported
* @param {String} filePath Ressource path
* @param {String} type Possible values: &apos;script&apos; or &apos;link&apos;
* @return {Boolean}
*/
isImported(filePath, type){
let imported = false,
importType = !type ? &apos;script&apos; : type,
attr = importType == &apos;script&apos; ? &apos;src&apos; : &apos;href&apos;,
files = Dom.tag(doc, importType);
for (let i=0, len=files.length; i&lt;len; i++){
if(files[i][attr] === undefined){
continue;
}
if(files[i][attr].match(filePath)){
imported = true;
break;
}
}
return imported;
}
/**
* Import script or stylesheet
* @param {String} fileId Ressource ID
* @param {String} filePath Ressource path
* @param {Function} callback Callback
* @param {String} type Possible values: &apos;script&apos; or &apos;link&apos;
*/
import(fileId, filePath, callback, type){
let ftype = !type ? &apos;script&apos; : type,
imported = this.isImported(filePath, ftype);
if(imported){
return;
}
let o = this,
isLoaded = false,
file,
head = Dom.tag(doc, &apos;head&apos;)[0];
if(Str.lower(ftype) === &apos;link&apos;){
file = Dom.create(
&apos;link&apos;,
[&apos;id&apos;, fileId], [&apos;type&apos;, &apos;text/css&apos;],
[&apos;rel&apos;, &apos;stylesheet&apos;], [&apos;href&apos;, filePath]
);
} else {
file = Dom.create(
&apos;script&apos;, [&apos;id&apos;, fileId],
[&apos;type&apos;, &apos;text/javascript&apos;], [&apos;src&apos;, filePath]
);
}
//Browser &lt;&gt; IE onload event works only for scripts, not for stylesheets
file.onload = file.onreadystatechange = function(){
if(!isLoaded &amp;&amp;
(!this.readyState || this.readyState === &apos;loaded&apos; ||
this.readyState === &apos;complete&apos;)){
isLoaded = true;
if(typeof callback === &apos;function&apos;){
callback.call(null, o);
}
}
};
file.onerror = function(){
throw new Error(&apos;TF script could not load: &apos; + filePath);
};
head.appendChild(file);
}
/**
* Check if table has filters grid
* @return {Boolean}
*/
hasGrid(){
return this._hasGrid;
}
/**
* Get list of filter IDs
* @return {[type]} [description]
*/
getFiltersId(){
return this.fltIds || [];
}
/**
* Get filtered (valid) rows indexes
* @param {Boolean} reCalc Force calculation of filtered rows list
* @return {Array} List of row indexes
*/
getValidRows(reCalc){
if(!reCalc){
return this.validRowsIndex;
}
this.validRowsIndex = [];
for(let k=this.refRow; k&lt;this.getRowsNb(true); k++){
let r = this.tbl.rows[k];
if(!this.paging){
if(this.getRowDisplay(r) !== &apos;none&apos;){
this.validRowsIndex.push(r.rowIndex);
}
} else {
if(r.getAttribute(&apos;validRow&apos;) === &apos;true&apos; ||
r.getAttribute(&apos;validRow&apos;) === null){
this.validRowsIndex.push(r.rowIndex);
}
}
}
return this.validRowsIndex;
}
/**
* Get the index of the row containing the filters
* @return {Number}
*/
getFiltersRowIndex(){
return this.filtersRowIndex;
}
/**
* Get the index of the headers row
* @return {Number}
*/
getHeadersRowIndex(){
return this.headersRow;
}
/**
* Get the row index from where the filtering process start (1st filterable
* row)
* @return {Number}
*/
getStartRowIndex(){
return this.refRow;
}
/**
* Get the index of the last row
* @return {Number}
*/
getLastRowIndex(){
if(!this._hasGrid){
return;
}
return (this.nbRows-1);
}
/**
* Get the header DOM element for a given column index
* @param {Number} colIndex Column index
* @return {Object}
*/
getHeaderElement(colIndex){
let table = this.gridLayout ? this.Mod.gridLayout.headTbl : this.tbl;
let tHead = Dom.tag(table, &apos;thead&apos;);
let headersRow = this.headersRow;
let header;
for(let i=0; i&lt;this.nbCells; i++){
if(i !== colIndex){
continue;
}
if(tHead.length === 0){
header = table.rows[headersRow].cells[i];
}
if(tHead.length === 1){
header = tHead[0].rows[headersRow].cells[i];
}
break;
}
return header;
}
/**
* Return the filter type for a specified column
* @param {Number} colIndex Column&apos;s index
* @return {String}
*/
getFilterType(colIndex){
let colType = this.cfg[&apos;col_&apos;+colIndex];
return !colType ? this.fltTypeInp : Str.lower(colType);
}
/**
* Get the total number of filterable rows
* @return {Number}
*/
getFilterableRowsNb(){
return this.getRowsNb(false);
}
/**
* Get the configuration object (literal object)
* @return {Object}
*/
config(){
return this.cfg;
}
}
</code></pre>
</div>
<footer class="footer">
Generated by <a href="https://esdoc.org">ESDoc<span data-ice="esdocVersion">(0.1.4)</span></a>
</footer>
<script src="script/search_index.js"></script>
<script src="script/search.js"></script>
<script src="script/pretty-print.js"></script>
<script src="script/inherited-summary.js"></script>
<script src="script/test-summary.js"></script>
<script src="script/inner-link.js"></script>
<script src="script/patch-for-local.js"></script>
</body>
</html>