1 | import {Feature} from '../../feature';
|
2 | import {tag} from '../../dom';
|
3 | import {INPUT} from '../../const';
|
4 | import {defaultsStr} from '../../settings';
|
5 | import {root} from '../../root';
|
6 |
|
7 | const INSTANTIATION_ERROR = `Failed to instantiate EditTable object.
|
8 | \n"ezEditTable" dependency not found.`;
|
9 |
|
10 | /**
|
11 | * Adapter module for ezEditTable, an external library providing advanced
|
12 | * grid features (selection and edition):
|
13 | * http://codecanyon.net/item/ezedittable-enhance-html-tables/2425123?ref=koalyptus
|
14 | */
|
15 | export default class AdapterEzEditTable extends Feature {
|
16 |
|
17 | /**
|
18 | * Creates an instance of AdapterEzEditTable
|
19 | *
|
20 | * @param {TableFilter} tf TableFilter instance
|
21 | * @param {Object} cfg Configuration options for ezEditTable library
|
22 | */
|
23 | constructor(tf, cfg) {
|
24 | super(tf, AdapterEzEditTable);
|
25 |
|
26 | /**
|
27 | * Module description
|
28 | * @type {String}
|
29 | */
|
30 | this.desc = defaultsStr(cfg.description, 'ezEditTable adapter');
|
31 |
|
32 | /**
|
33 | * Filename of ezEditTable library
|
34 | * @type {String}
|
35 | */
|
36 | this.filename = defaultsStr(cfg.filename, 'ezEditTable.js');
|
37 |
|
38 | /**
|
39 | * Path to ezEditTable library
|
40 | * @type {String}
|
41 | */
|
42 | this.vendorPath = cfg.vendor_path;
|
43 |
|
44 | /**
|
45 | * Load ezEditTable stylesheet
|
46 | * @type {Boolean}
|
47 | */
|
48 | this.loadStylesheet = Boolean(cfg.load_stylesheet);
|
49 |
|
50 | /**
|
51 | * Path to ezEditTable stylesheet
|
52 | * @type {String}
|
53 | */
|
54 | this.stylesheet = defaultsStr(cfg.stylesheet,
|
55 | this.vendorPath + 'ezEditTable.css');
|
56 |
|
57 | /**
|
58 | * Name of ezEditTable stylesheet
|
59 | * @type {String}
|
60 | */
|
61 | this.stylesheetName = defaultsStr(cfg.stylesheet_name,
|
62 | 'ezEditTableCss');
|
63 |
|
64 | // Enable the ezEditTable's scroll into view behaviour if grid layout on
|
65 | cfg.scroll_into_view = cfg.scroll_into_view === false ?
|
66 | false : tf.gridLayout;
|
67 |
|
68 | /**
|
69 | * ezEditTable instance
|
70 | * @type {EditTable}
|
71 | * @private
|
72 | */
|
73 | this._ezEditTable = null;
|
74 |
|
75 | /**
|
76 | * ezEditTable configuration
|
77 | * @private
|
78 | */
|
79 | this.cfg = cfg;
|
80 |
|
81 | this.enable();
|
82 | }
|
83 |
|
84 | /**
|
85 | * Conditionally load ezEditTable library and set advanced grid
|
86 | */
|
87 | init() {
|
88 | if (this.initialized) {
|
89 | return;
|
90 | }
|
91 | let tf = this.tf;
|
92 | if (root.EditTable) {
|
93 | this._setAdvancedGrid();
|
94 | } else {
|
95 | let path = this.vendorPath + this.filename;
|
96 | tf.import(this.filename, path, () => this._setAdvancedGrid());
|
97 | }
|
98 | if (this.loadStylesheet && !tf.isImported(this.stylesheet, 'link')) {
|
99 | tf.import(this.stylesheetName, this.stylesheet, null, 'link');
|
100 | }
|
101 |
|
102 | // TODO: hack to prevent ezEditTable enter key event hijaking.
|
103 | // Needs to be fixed in the vendor's library
|
104 | this.emitter.on(['filter-focus', 'filter-blur'],
|
105 | () => this._toggleForInputFilter());
|
106 |
|
107 | /**
|
108 | * @inherited
|
109 | */
|
110 | this.initialized = true;
|
111 | }
|
112 |
|
113 | /**
|
114 | * Instantiate ezEditTable component for advanced grid features
|
115 | * @private
|
116 | */
|
117 | _setAdvancedGrid() {
|
118 | let tf = this.tf;
|
119 |
|
120 | //start row for EditTable constructor needs to be calculated
|
121 | let startRow,
|
122 | cfg = this.cfg,
|
123 | thead = tag(tf.dom(), 'thead');
|
124 |
|
125 | //if thead exists and startRow not specified, startRow is calculated
|
126 | //automatically by EditTable
|
127 | if (thead.length > 0 && !cfg.startRow) {
|
128 | startRow = undefined;
|
129 | }
|
130 | //otherwise startRow config property if any or TableFilter refRow
|
131 | else {
|
132 | startRow = cfg.startRow || tf.refRow;
|
133 | }
|
134 |
|
135 | cfg.base_path = cfg.base_path || tf.basePath + 'ezEditTable/';
|
136 | let editable = cfg.editable;
|
137 | let selectable = cfg.selection;
|
138 |
|
139 | if (selectable) {
|
140 | cfg.default_selection = cfg.default_selection || 'row';
|
141 | }
|
142 | //CSS Styles
|
143 | cfg.active_cell_css = cfg.active_cell_css || 'ezETSelectedCell';
|
144 |
|
145 | let _lastValidRowIndex = 0;
|
146 | let _lastRowIndex = 0;
|
147 |
|
148 | if (selectable) {
|
149 | //Row navigation needs to be calculated according to TableFilter's
|
150 | //validRowsIndex array
|
151 | let onAfterSelection = function (et, selectedElm, e) {
|
152 | let slc = et.Selection;
|
153 | //Next valid filtered row needs to be selected
|
154 | let doSelect = function (nextRowIndex) {
|
155 | if (et.defaultSelection === 'row') {
|
156 | /* eslint-disable */
|
157 | slc.SelectRowByIndex(nextRowIndex);
|
158 | /* eslint-enable */
|
159 | } else {
|
160 | /* eslint-disable */
|
161 | et.ClearSelections();
|
162 | /* eslint-enable */
|
163 | let cellIndex = selectedElm.cellIndex,
|
164 | row = tf.dom().rows[nextRowIndex];
|
165 | if (et.defaultSelection === 'both') {
|
166 | /* eslint-disable */
|
167 | slc.SelectRowByIndex(nextRowIndex);
|
168 | /* eslint-enable */
|
169 | }
|
170 | if (row) {
|
171 | /* eslint-disable */
|
172 | slc.SelectCell(row.cells[cellIndex]);
|
173 | /* eslint-enable */
|
174 | }
|
175 | }
|
176 | //Table is filtered
|
177 | if (tf.validRowsIndex.length !== tf.getRowsNb()) {
|
178 | let r = tf.dom().rows[nextRowIndex];
|
179 | if (r) {
|
180 | r.scrollIntoView(false);
|
181 | }
|
182 | if (cell) {
|
183 | if (cell.cellIndex === (tf.getCellsNb() - 1) &&
|
184 | tf.gridLayout) {
|
185 | tf.tblCont.scrollLeft = 100000000;
|
186 | }
|
187 | else if (cell.cellIndex === 0 && tf.gridLayout) {
|
188 | tf.tblCont.scrollLeft = 0;
|
189 | } else {
|
190 | cell.scrollIntoView(false);
|
191 | }
|
192 | }
|
193 | }
|
194 | };
|
195 |
|
196 | //table is not filtered
|
197 | if (!tf.validRowsIndex) {
|
198 | return;
|
199 | }
|
200 | let validIndexes = tf.validRowsIndex,
|
201 | validIdxLen = validIndexes.length,
|
202 | row = et.defaultSelection !== 'row' ?
|
203 | selectedElm.parentNode : selectedElm,
|
204 | //cell for default_selection = 'both' or 'cell'
|
205 | cell = selectedElm.nodeName === 'TD' ? selectedElm : null,
|
206 | /* eslint-disable */
|
207 | keyCode = e !== undefined ? et.Event.GetKey(e) : 0,
|
208 | /* eslint-enable */
|
209 | isRowValid = validIndexes.indexOf(row.rowIndex) !== -1,
|
210 | nextRowIndex,
|
211 | paging = tf.feature('paging'),
|
212 | //pgup/pgdown keys
|
213 | d = keyCode === 34 || keyCode === 33 ?
|
214 | (paging && paging.pageLength || et.nbRowsPerPage) :
|
215 | 1;
|
216 |
|
217 | //If next row is not valid, next valid filtered row needs to be
|
218 | //calculated
|
219 | if (!isRowValid) {
|
220 | //Selection direction up/down
|
221 | if (row.rowIndex > _lastRowIndex) {
|
222 | //last row
|
223 | if (row.rowIndex >= validIndexes[validIdxLen - 1]) {
|
224 | nextRowIndex = validIndexes[validIdxLen - 1];
|
225 | } else {
|
226 | let calcRowIndex = (_lastValidRowIndex + d);
|
227 | if (calcRowIndex > (validIdxLen - 1)) {
|
228 | nextRowIndex = validIndexes[validIdxLen - 1];
|
229 | } else {
|
230 | nextRowIndex = validIndexes[calcRowIndex];
|
231 | }
|
232 | }
|
233 | } else {
|
234 | //first row
|
235 | if (row.rowIndex <= validIndexes[0]) {
|
236 | nextRowIndex = validIndexes[0];
|
237 | } else {
|
238 | let v = validIndexes[_lastValidRowIndex - d];
|
239 | nextRowIndex = v ? v : validIndexes[0];
|
240 | }
|
241 | }
|
242 | _lastRowIndex = row.rowIndex;
|
243 | doSelect(nextRowIndex);
|
244 | } else {
|
245 | //If filtered row is valid, special calculation for
|
246 | //pgup/pgdown keys
|
247 | if (keyCode !== 34 && keyCode !== 33) {
|
248 | _lastValidRowIndex = validIndexes.indexOf(row.rowIndex);
|
249 | _lastRowIndex = row.rowIndex;
|
250 | } else {
|
251 | if (keyCode === 34) { //pgdown
|
252 | //last row
|
253 | if ((_lastValidRowIndex + d) <= (validIdxLen - 1)) {
|
254 | nextRowIndex = validIndexes[
|
255 | _lastValidRowIndex + d];
|
256 | } else {
|
257 | nextRowIndex = [validIdxLen - 1];
|
258 | }
|
259 | } else { //pgup
|
260 | //first row
|
261 | if ((_lastValidRowIndex - d) <= validIndexes[0]) {
|
262 | nextRowIndex = validIndexes[0];
|
263 | } else {
|
264 | nextRowIndex = validIndexes[
|
265 | _lastValidRowIndex - d];
|
266 | }
|
267 | }
|
268 | _lastRowIndex = nextRowIndex;
|
269 | _lastValidRowIndex = validIndexes.indexOf(nextRowIndex);
|
270 | doSelect(nextRowIndex);
|
271 | }
|
272 | }
|
273 | };
|
274 |
|
275 | //Page navigation has to be enforced whenever selected row is out of
|
276 | //the current page range
|
277 | let onBeforeSelection = function (et, selectedElm) {
|
278 | let row = et.defaultSelection !== 'row' ?
|
279 | selectedElm.parentNode : selectedElm;
|
280 | if (tf.paging) {
|
281 | if (tf.feature('paging').nbPages > 1) {
|
282 | let paging = tf.feature('paging');
|
283 | //page length is re-assigned in case it has changed
|
284 | et.nbRowsPerPage = paging.pageLength;
|
285 | let validIndexes = tf.validRowsIndex,
|
286 | validIdxLen = validIndexes.length,
|
287 | pagingEndRow = parseInt(paging.startPagingRow, 10) +
|
288 | parseInt(paging.pageLength, 10);
|
289 | let rowIndex = row.rowIndex;
|
290 |
|
291 | if ((rowIndex === validIndexes[validIdxLen - 1]) &&
|
292 | paging.currentPageNb !== paging.nbPages) {
|
293 | paging.setPage('last');
|
294 | }
|
295 | else if ((rowIndex === validIndexes[0]) &&
|
296 | paging.currentPageNb !== 1) {
|
297 | paging.setPage('first');
|
298 | }
|
299 | else if (rowIndex > validIndexes[pagingEndRow - 1] &&
|
300 | rowIndex < validIndexes[validIdxLen - 1]) {
|
301 | paging.setPage('next');
|
302 | }
|
303 | else if (
|
304 | rowIndex < validIndexes[paging.startPagingRow] &&
|
305 | rowIndex > validIndexes[0]) {
|
306 | paging.setPage('previous');
|
307 | }
|
308 | }
|
309 | }
|
310 | };
|
311 |
|
312 | //Selected row needs to be visible when paging is activated
|
313 | if (tf.paging) {
|
314 | tf.feature('paging').onAfterChangePage = function (paging) {
|
315 | let advGrid = paging.tf.extension('advancedGrid');
|
316 | let et = advGrid._ezEditTable;
|
317 | let slc = et.Selection;
|
318 | /* eslint-disable */
|
319 | let row = slc.GetActiveRow();
|
320 | /* eslint-enable */
|
321 | if (row) {
|
322 | row.scrollIntoView(false);
|
323 | }
|
324 | /* eslint-disable */
|
325 | let cell = slc.GetActiveCell();
|
326 | /* eslint-enable */
|
327 | if (cell) {
|
328 | cell.scrollIntoView(false);
|
329 | }
|
330 | };
|
331 | }
|
332 |
|
333 | //Rows navigation when rows are filtered is performed with the
|
334 | //EditTable row selection callback events
|
335 | if (cfg.default_selection === 'row') {
|
336 | let fnB = cfg.on_before_selected_row;
|
337 | cfg.on_before_selected_row = function () {
|
338 | var args = arguments;
|
339 | onBeforeSelection(args[0], args[1], args[2]);
|
340 | if (fnB) {
|
341 | fnB.call(null, args[0], args[1], args[2]);
|
342 | }
|
343 | };
|
344 | let fnA = cfg.on_after_selected_row;
|
345 | cfg.on_after_selected_row = function () {
|
346 | var args = arguments;
|
347 | onAfterSelection(args[0], args[1], args[2]);
|
348 | if (fnA) {
|
349 | fnA.call(null, args[0], args[1], args[2]);
|
350 | }
|
351 | };
|
352 | } else {
|
353 | let fnD = cfg.on_before_selected_cell;
|
354 | cfg.on_before_selected_cell = function () {
|
355 | var args = arguments;
|
356 | onBeforeSelection(args[0], args[1], args[2]);
|
357 | if (fnD) {
|
358 | fnD.call(null, args[0], args[1], args[2]);
|
359 | }
|
360 | };
|
361 | let fnC = cfg.on_after_selected_cell;
|
362 | cfg.on_after_selected_cell = function () {
|
363 | var args = arguments;
|
364 | onAfterSelection(args[0], args[1], args[2]);
|
365 | if (fnC) {
|
366 | fnC.call(null, args[0], args[1], args[2]);
|
367 | }
|
368 | };
|
369 | }
|
370 | }
|
371 | if (editable) {
|
372 | //Added or removed rows, TF rows number needs to be re-calculated
|
373 | let fnE = cfg.on_added_dom_row;
|
374 | cfg.on_added_dom_row = function () {
|
375 | var args = arguments;
|
376 | tf.nbFilterableRows++;
|
377 | if (!tf.paging) {
|
378 | tf.emitter.emit('rows-changed', tf, this);
|
379 | } else {
|
380 | tf.nbFilterableRows++;
|
381 | tf.paging = false;
|
382 | tf.feature('paging').destroy();
|
383 | tf.feature('paging').reset();
|
384 | }
|
385 | if (tf.alternateRows) {
|
386 | tf.feature('alternateRows').init();
|
387 | }
|
388 | if (fnE) {
|
389 | fnE.call(null, args[0], args[1], args[2]);
|
390 | }
|
391 | };
|
392 | if (cfg.actions && cfg.actions['delete']) {
|
393 | let fnF = cfg.actions['delete'].on_after_submit;
|
394 | cfg.actions['delete'].on_after_submit = function () {
|
395 | var args = arguments;
|
396 | tf.nbFilterableRows--;
|
397 | if (!tf.paging) {
|
398 | tf.emitter.emit('rows-changed', tf, this);
|
399 | } else {
|
400 | tf.nbFilterableRows--;
|
401 | tf.paging = false;
|
402 | tf.feature('paging').destroy();
|
403 | tf.feature('paging').reset(false);
|
404 | }
|
405 | if (tf.alternateRows) {
|
406 | tf.feature('alternateRows').init();
|
407 | }
|
408 | if (fnF) {
|
409 | fnF.call(null, args[0], args[1]);
|
410 | }
|
411 | };
|
412 | }
|
413 | }
|
414 |
|
415 | try {
|
416 | /* eslint-disable */
|
417 | this._ezEditTable = new EditTable(tf.id, cfg, startRow);
|
418 | this._ezEditTable.Init();
|
419 | /* eslint-enable */
|
420 | } catch (e) { throw new Error(INSTANTIATION_ERROR); }
|
421 |
|
422 | this.initialized = true;
|
423 | }
|
424 |
|
425 | /**
|
426 | * Reset advanced grid when previously removed
|
427 | */
|
428 | reset() {
|
429 | let ezEditTable = this._ezEditTable;
|
430 | if (ezEditTable) {
|
431 | if (this.cfg.selection) {
|
432 | /* eslint-disable */
|
433 | ezEditTable.Selection.Set();
|
434 | /* eslint-enable */
|
435 | }
|
436 | if (this.cfg.editable) {
|
437 | /* eslint-disable */
|
438 | ezEditTable.Editable.Set();
|
439 | /* eslint-enable */
|
440 | }
|
441 | }
|
442 | }
|
443 |
|
444 | /**
|
445 | * Toggle behaviour
|
446 | */
|
447 | toggle() {
|
448 | let ezEditTable = this._ezEditTable;
|
449 | if (ezEditTable.editable) {
|
450 | /* eslint-disable */
|
451 | ezEditTable.Editable.Remove();
|
452 | /* eslint-enable */
|
453 | } else {
|
454 | /* eslint-disable */
|
455 | ezEditTable.Editable.Set();
|
456 | /* eslint-enable */
|
457 | }
|
458 | if (ezEditTable.selection) {
|
459 | /* eslint-disable */
|
460 | ezEditTable.Selection.Remove();
|
461 | /* eslint-enable */
|
462 | } else {
|
463 | /* eslint-disable */
|
464 | ezEditTable.Selection.Set();
|
465 | /* eslint-enable */
|
466 | }
|
467 | }
|
468 |
|
469 | _toggleForInputFilter() {
|
470 | let tf = this.tf;
|
471 | if (!tf.getActiveFilterId()) {
|
472 | return;
|
473 | }
|
474 | let colIndex = tf.getColumnIndexFromFilterId(tf.getActiveFilterId());
|
475 | let filterType = tf.getFilterType(colIndex);
|
476 | if (filterType === INPUT) {
|
477 | this.toggle();
|
478 | }
|
479 | }
|
480 |
|
481 | /**
|
482 | * Remove advanced grid
|
483 | */
|
484 | destroy() {
|
485 | if (!this.initialized) {
|
486 | return;
|
487 | }
|
488 | let ezEditTable = this._ezEditTable;
|
489 | if (ezEditTable) {
|
490 | if (this.cfg.selection) {
|
491 | /* eslint-disable */
|
492 | ezEditTable.Selection.ClearSelections();
|
493 | ezEditTable.Selection.Remove();
|
494 | /* eslint-enable */
|
495 | }
|
496 | if (this.cfg.editable) {
|
497 | /* eslint-disable */
|
498 | ezEditTable.Editable.Remove();
|
499 | /* eslint-enable */
|
500 | }
|
501 | }
|
502 |
|
503 | this.emitter.off(['filter-focus', 'filter-blur'],
|
504 | () => this._toggleForInputFilter());
|
505 | this.initialized = false;
|
506 | }
|
507 | }
|
508 |
|
509 | AdapterEzEditTable.meta = {altName: 'advancedGrid'};
|