import {Feature} from '../../feature'; import {tag} from '../../dom'; import {INPUT} from '../../const'; import {defaultsStr} from '../../settings'; import {root} from '../../root'; const INSTANTIATION_ERROR = `Failed to instantiate EditTable object. \n"ezEditTable" dependency not found.`; /** * Adapter module for ezEditTable, an external library providing advanced * grid features (selection and edition): * http://codecanyon.net/item/ezedittable-enhance-html-tables/2425123?ref=koalyptus */ export default class AdapterEzEditTable extends Feature { /** * Creates an instance of AdapterEzEditTable * * @param {TableFilter} tf TableFilter instance * @param {Object} cfg Configuration options for ezEditTable library */ constructor(tf, cfg) { super(tf, AdapterEzEditTable); /** * Module description * @type {String} */ this.desc = defaultsStr(cfg.description, 'ezEditTable adapter'); /** * Filename of ezEditTable library * @type {String} */ this.filename = defaultsStr(cfg.filename, 'ezEditTable.js'); /** * Path to ezEditTable library * @type {String} */ this.vendorPath = cfg.vendor_path; /** * Load ezEditTable stylesheet * @type {Boolean} */ this.loadStylesheet = Boolean(cfg.load_stylesheet); /** * Path to ezEditTable stylesheet * @type {String} */ this.stylesheet = defaultsStr(cfg.stylesheet, this.vendorPath + 'ezEditTable.css'); /** * Name of ezEditTable stylesheet * @type {String} */ this.stylesheetName = defaultsStr(cfg.stylesheet_name, 'ezEditTableCss'); // Enable the ezEditTable's scroll into view behaviour if grid layout on cfg.scroll_into_view = cfg.scroll_into_view === false ? false : tf.gridLayout; /** * ezEditTable instance * @type {EditTable} * @private */ this._ezEditTable = null; /** * ezEditTable configuration * @private */ this.cfg = cfg; this.enable(); } /** * Conditionally load ezEditTable library and set advanced grid */ init() { if (this.initialized) { return; } let tf = this.tf; if (root.EditTable) { this._setAdvancedGrid(); } else { let path = this.vendorPath + this.filename; tf.import(this.filename, path, () => this._setAdvancedGrid()); } if (this.loadStylesheet && !tf.isImported(this.stylesheet, 'link')) { tf.import(this.stylesheetName, this.stylesheet, null, 'link'); } // TODO: hack to prevent ezEditTable enter key event hijaking. // Needs to be fixed in the vendor's library this.emitter.on(['filter-focus', 'filter-blur'], () => this._toggleForInputFilter()); /** * @inherited */ this.initialized = true; } /** * Instantiate ezEditTable component for advanced grid features * @private */ _setAdvancedGrid() { let tf = this.tf; //start row for EditTable constructor needs to be calculated let startRow, cfg = this.cfg, thead = tag(tf.dom(), 'thead'); //if thead exists and startRow not specified, startRow is calculated //automatically by EditTable if (thead.length > 0 && !cfg.startRow) { startRow = undefined; } //otherwise startRow config property if any or TableFilter refRow else { startRow = cfg.startRow || tf.refRow; } cfg.base_path = cfg.base_path || tf.basePath + 'ezEditTable/'; let editable = cfg.editable; let selectable = cfg.selection; if (selectable) { cfg.default_selection = cfg.default_selection || 'row'; } //CSS Styles cfg.active_cell_css = cfg.active_cell_css || 'ezETSelectedCell'; let _lastValidRowIndex = 0; let _lastRowIndex = 0; if (selectable) { //Row navigation needs to be calculated according to TableFilter's //validRowsIndex array let onAfterSelection = function (et, selectedElm, e) { let slc = et.Selection; //Next valid filtered row needs to be selected let doSelect = function (nextRowIndex) { if (et.defaultSelection === 'row') { /* eslint-disable */ slc.SelectRowByIndex(nextRowIndex); /* eslint-enable */ } else { /* eslint-disable */ et.ClearSelections(); /* eslint-enable */ let cellIndex = selectedElm.cellIndex, row = tf.dom().rows[nextRowIndex]; if (et.defaultSelection === 'both') { /* eslint-disable */ slc.SelectRowByIndex(nextRowIndex); /* eslint-enable */ } if (row) { /* eslint-disable */ slc.SelectCell(row.cells[cellIndex]); /* eslint-enable */ } } //Table is filtered if (tf.validRowsIndex.length !== tf.getRowsNb()) { let r = tf.dom().rows[nextRowIndex]; if (r) { r.scrollIntoView(false); } if (cell) { if (cell.cellIndex === (tf.getCellsNb() - 1) && tf.gridLayout) { tf.tblCont.scrollLeft = 100000000; } else if (cell.cellIndex === 0 && tf.gridLayout) { tf.tblCont.scrollLeft = 0; } else { cell.scrollIntoView(false); } } } }; //table is not filtered if (!tf.validRowsIndex) { return; } let validIndexes = tf.validRowsIndex, validIdxLen = validIndexes.length, row = et.defaultSelection !== 'row' ? selectedElm.parentNode : selectedElm, //cell for default_selection = 'both' or 'cell' cell = selectedElm.nodeName === 'TD' ? selectedElm : null, /* eslint-disable */ keyCode = e !== undefined ? et.Event.GetKey(e) : 0, /* eslint-enable */ isRowValid = validIndexes.indexOf(row.rowIndex) !== -1, nextRowIndex, paging = tf.feature('paging'), //pgup/pgdown keys d = keyCode === 34 || keyCode === 33 ? (paging && paging.pageLength || et.nbRowsPerPage) : 1; //If next row is not valid, next valid filtered row needs to be //calculated if (!isRowValid) { //Selection direction up/down if (row.rowIndex > _lastRowIndex) { //last row if (row.rowIndex >= validIndexes[validIdxLen - 1]) { nextRowIndex = validIndexes[validIdxLen - 1]; } else { let calcRowIndex = (_lastValidRowIndex + d); if (calcRowIndex > (validIdxLen - 1)) { nextRowIndex = validIndexes[validIdxLen - 1]; } else { nextRowIndex = validIndexes[calcRowIndex]; } } } else { //first row if (row.rowIndex <= validIndexes[0]) { nextRowIndex = validIndexes[0]; } else { let v = validIndexes[_lastValidRowIndex - d]; nextRowIndex = v ? v : validIndexes[0]; } } _lastRowIndex = row.rowIndex; doSelect(nextRowIndex); } else { //If filtered row is valid, special calculation for //pgup/pgdown keys if (keyCode !== 34 && keyCode !== 33) { _lastValidRowIndex = validIndexes.indexOf(row.rowIndex); _lastRowIndex = row.rowIndex; } else { if (keyCode === 34) { //pgdown //last row if ((_lastValidRowIndex + d) <= (validIdxLen - 1)) { nextRowIndex = validIndexes[ _lastValidRowIndex + d]; } else { nextRowIndex = [validIdxLen - 1]; } } else { //pgup //first row if ((_lastValidRowIndex - d) <= validIndexes[0]) { nextRowIndex = validIndexes[0]; } else { nextRowIndex = validIndexes[ _lastValidRowIndex - d]; } } _lastRowIndex = nextRowIndex; _lastValidRowIndex = validIndexes.indexOf(nextRowIndex); doSelect(nextRowIndex); } } }; //Page navigation has to be enforced whenever selected row is out of //the current page range let onBeforeSelection = function (et, selectedElm) { let row = et.defaultSelection !== 'row' ? selectedElm.parentNode : selectedElm; if (tf.paging) { if (tf.feature('paging').nbPages > 1) { let paging = tf.feature('paging'); //page length is re-assigned in case it has changed et.nbRowsPerPage = paging.pageLength; let validIndexes = tf.validRowsIndex, validIdxLen = validIndexes.length, pagingEndRow = parseInt(paging.startPagingRow, 10) + parseInt(paging.pageLength, 10); let rowIndex = row.rowIndex; if ((rowIndex === validIndexes[validIdxLen - 1]) && paging.currentPageNb !== paging.nbPages) { paging.setPage('last'); } else if ((rowIndex === validIndexes[0]) && paging.currentPageNb !== 1) { paging.setPage('first'); } else if (rowIndex > validIndexes[pagingEndRow - 1] && rowIndex < validIndexes[validIdxLen - 1]) { paging.setPage('next'); } else if ( rowIndex < validIndexes[paging.startPagingRow] && rowIndex > validIndexes[0]) { paging.setPage('previous'); } } } }; //Selected row needs to be visible when paging is activated if (tf.paging) { tf.feature('paging').onAfterChangePage = function (paging) { let advGrid = paging.tf.extension('advancedGrid'); let et = advGrid._ezEditTable; let slc = et.Selection; /* eslint-disable */ let row = slc.GetActiveRow(); /* eslint-enable */ if (row) { row.scrollIntoView(false); } /* eslint-disable */ let cell = slc.GetActiveCell(); /* eslint-enable */ if (cell) { cell.scrollIntoView(false); } }; } //Rows navigation when rows are filtered is performed with the //EditTable row selection callback events if (cfg.default_selection === 'row') { let fnB = cfg.on_before_selected_row; cfg.on_before_selected_row = function () { var args = arguments; onBeforeSelection(args[0], args[1], args[2]); if (fnB) { fnB.call(null, args[0], args[1], args[2]); } }; let fnA = cfg.on_after_selected_row; cfg.on_after_selected_row = function () { var args = arguments; onAfterSelection(args[0], args[1], args[2]); if (fnA) { fnA.call(null, args[0], args[1], args[2]); } }; } else { let fnD = cfg.on_before_selected_cell; cfg.on_before_selected_cell = function () { var args = arguments; onBeforeSelection(args[0], args[1], args[2]); if (fnD) { fnD.call(null, args[0], args[1], args[2]); } }; let fnC = cfg.on_after_selected_cell; cfg.on_after_selected_cell = function () { var args = arguments; onAfterSelection(args[0], args[1], args[2]); if (fnC) { fnC.call(null, args[0], args[1], args[2]); } }; } } if (editable) { //Added or removed rows, TF rows number needs to be re-calculated let fnE = cfg.on_added_dom_row; cfg.on_added_dom_row = function () { var args = arguments; tf.nbFilterableRows++; if (!tf.paging) { tf.emitter.emit('rows-changed', tf, this); } else { tf.nbFilterableRows++; tf.paging = false; tf.feature('paging').destroy(); tf.feature('paging').reset(); } if (tf.alternateRows) { tf.feature('alternateRows').init(); } if (fnE) { fnE.call(null, args[0], args[1], args[2]); } }; if (cfg.actions && cfg.actions['delete']) { let fnF = cfg.actions['delete'].on_after_submit; cfg.actions['delete'].on_after_submit = function () { var args = arguments; tf.nbFilterableRows--; if (!tf.paging) { tf.emitter.emit('rows-changed', tf, this); } else { tf.nbFilterableRows--; tf.paging = false; tf.feature('paging').destroy(); tf.feature('paging').reset(false); } if (tf.alternateRows) { tf.feature('alternateRows').init(); } if (fnF) { fnF.call(null, args[0], args[1]); } }; } } try { /* eslint-disable */ this._ezEditTable = new EditTable(tf.id, cfg, startRow); this._ezEditTable.Init(); /* eslint-enable */ } catch (e) { throw new Error(INSTANTIATION_ERROR); } this.initialized = true; } /** * Reset advanced grid when previously removed */ reset() { let ezEditTable = this._ezEditTable; if (ezEditTable) { if (this.cfg.selection) { /* eslint-disable */ ezEditTable.Selection.Set(); /* eslint-enable */ } if (this.cfg.editable) { /* eslint-disable */ ezEditTable.Editable.Set(); /* eslint-enable */ } } } /** * Toggle behaviour */ toggle() { let ezEditTable = this._ezEditTable; if (ezEditTable.editable) { /* eslint-disable */ ezEditTable.Editable.Remove(); /* eslint-enable */ } else { /* eslint-disable */ ezEditTable.Editable.Set(); /* eslint-enable */ } if (ezEditTable.selection) { /* eslint-disable */ ezEditTable.Selection.Remove(); /* eslint-enable */ } else { /* eslint-disable */ ezEditTable.Selection.Set(); /* eslint-enable */ } } _toggleForInputFilter() { let tf = this.tf; if (!tf.getActiveFilterId()) { return; } let colIndex = tf.getColumnIndexFromFilterId(tf.getActiveFilterId()); let filterType = tf.getFilterType(colIndex); if (filterType === INPUT) { this.toggle(); } } /** * Remove advanced grid */ destroy() { if (!this.initialized) { return; } let ezEditTable = this._ezEditTable; if (ezEditTable) { if (this.cfg.selection) { /* eslint-disable */ ezEditTable.Selection.ClearSelections(); ezEditTable.Selection.Remove(); /* eslint-enable */ } if (this.cfg.editable) { /* eslint-disable */ ezEditTable.Editable.Remove(); /* eslint-enable */ } } this.emitter.off(['filter-focus', 'filter-blur'], () => this._toggleForInputFilter()); this.initialized = false; } } AdapterEzEditTable.meta = {altName: 'advancedGrid'};