otsdaq_utilities  v2_05_02_indev
column_manager.js
1 var ColumnManager = function(table){
2  this.table = table; //hold parent table
3  this.blockHozScrollEvent = false;
4  this.headersElement = this.createHeadersElement();
5  this.element = this.createHeaderElement(); //containing element
6  this.rowManager = null; //hold row manager object
7  this.columns = []; // column definition object
8  this.columnsByIndex = []; //columns by index
9  this.columnsByField = {}; //columns by field
10  this.scrollLeft = 0;
11 
12  this.element.insertBefore(this.headersElement, this.element.firstChild);
13 };
14 
16 
17 ColumnManager.prototype.createHeadersElement = function (){
18  var el = document.createElement("div");
19 
20  el.classList.add("tabulator-headers");
21 
22  return el;
23 };
24 
25 ColumnManager.prototype.createHeaderElement = function (){
26  var el = document.createElement("div");
27 
28  el.classList.add("tabulator-header");
29 
30  if(!this.table.options.headerVisible){
31  el.classList.add("tabulator-header-hidden");
32  }
33 
34  return el;
35 };
36 
37 ColumnManager.prototype.initialize = function (){
38  var self = this;
39 
40  //scroll body along with header
41  // self.element.addEventListener("scroll", function(e){
42  // if(!self.blockHozScrollEvent){
43  // self.table.rowManager.scrollHorizontal(self.element.scrollLeft);
44  // }
45  // });
46 };
47 
48 
49 //link to row manager
50 ColumnManager.prototype.setRowManager = function(manager){
51  this.rowManager = manager;
52 };
53 
54 //return containing element
55 ColumnManager.prototype.getElement = function(){
56  return this.element;
57 };
58 
59 //return header containing element
60 ColumnManager.prototype.getHeadersElement = function(){
61  return this.headersElement;
62 };
63 
64 // ColumnManager.prototype.tempScrollBlock = function(){
65 // clearTimeout(this.blockHozScrollEvent);
66 // this.blockHozScrollEvent = setTimeout(() => {this.blockHozScrollEvent = false;}, 50);
67 // }
68 
69 //scroll horizontally to match table body
70 ColumnManager.prototype.scrollHorizontal = function(left){
71  var hozAdjust = 0,
72  scrollWidth = this.element.scrollWidth - this.table.element.clientWidth;
73 
74  // this.tempScrollBlock();
75  this.element.scrollLeft = left;
76 
77  //adjust for vertical scrollbar moving table when present
78  if(left > scrollWidth){
79  hozAdjust = left - scrollWidth;
80  this.element.style.marginLeft = (-(hozAdjust)) + "px";
81  }else{
82  this.element.style.marginLeft = 0;
83  }
84 
85  //keep frozen columns fixed in position
86  //this._calcFrozenColumnsPos(hozAdjust + 3);
87 
88  this.scrollLeft = left;
89 
90  if(this.table.modExists("frozenColumns")){
91  this.table.modules.frozenColumns.scrollHorizontal();
92  }
93 };
94 
95 
97 
98 ColumnManager.prototype.generateColumnsFromRowData = function(data){
99  var cols = [],
100  row, sorter;
101 
102  if(data && data.length){
103 
104  row = data[0];
105 
106  for(var key in row){
107  let col = {
108  field:key,
109  title:key,
110  };
111 
112  let value = row[key];
113 
114  switch(typeof value){
115  case "undefined":
116  sorter = "string";
117  break;
118 
119  case "boolean":
120  sorter = "boolean";
121  break;
122 
123  case "object":
124  if(Array.isArray(value)){
125  sorter = "array";
126  }else{
127  sorter = "string";
128  }
129  break;
130 
131  default:
132  if(!isNaN(value) && value !== ""){
133  sorter = "number";
134  }else{
135  if(value.match(/((^[0-9]+[a-z]+)|(^[a-z]+[0-9]+))+$/i)){
136  sorter = "alphanum";
137  }else{
138  sorter = "string";
139  }
140  }
141  break;
142  }
143 
144  col.sorter = sorter;
145 
146  cols.push(col);
147  }
148 
149  this.table.options.columns = cols;
150  this.setColumns(this.table.options.columns);
151  }
152 };
153 
154 ColumnManager.prototype.setColumns = function(cols, row){
155  var self = this;
156 
157  while(self.headersElement.firstChild) self.headersElement.removeChild(self.headersElement.firstChild);
158 
159  self.columns = [];
160  self.columnsByIndex = [];
161  self.columnsByField = {};
162 
163 
164  //reset frozen columns
165  if(self.table.modExists("frozenColumns")){
166  self.table.modules.frozenColumns.reset();
167  }
168 
169  cols.forEach(function(def, i){
170  self._addColumn(def);
171  });
172 
173  self._reIndexColumns();
174 
175  if(self.table.options.responsiveLayout && self.table.modExists("responsiveLayout", true)){
176  self.table.modules.responsiveLayout.initialize();
177  }
178 
179  self.redraw(true);
180 };
181 
182 ColumnManager.prototype._addColumn = function(definition, before, nextToColumn){
183  var column = new Column(definition, this),
184  colEl = column.getElement(),
185  index = nextToColumn ? this.findColumnIndex(nextToColumn) : nextToColumn;
186 
187  if(nextToColumn && index > -1){
188 
189  var parentIndex = this.columns.indexOf(nextToColumn.getTopColumn());
190  var nextEl = nextToColumn.getElement();
191 
192  if(before){
193  this.columns.splice(parentIndex, 0, column);
194  nextEl.parentNode.insertBefore(colEl, nextEl);
195  }else{
196  this.columns.splice(parentIndex + 1, 0, column);
197  nextEl.parentNode.insertBefore(colEl, nextEl.nextSibling);
198  }
199 
200  }else{
201  if(before){
202  this.columns.unshift(column);
203  this.headersElement.insertBefore(column.getElement(), this.headersElement.firstChild);
204  }else{
205  this.columns.push(column);
206  this.headersElement.appendChild(column.getElement());
207  }
208 
209  column.columnRendered();
210  }
211 
212  return column;
213 };
214 
215 ColumnManager.prototype.registerColumnField = function(col){
216  if(col.definition.field){
217  this.columnsByField[col.definition.field] = col;
218  }
219 };
220 
221 ColumnManager.prototype.registerColumnPosition = function(col){
222  this.columnsByIndex.push(col);
223 };
224 
225 ColumnManager.prototype._reIndexColumns = function(){
226  this.columnsByIndex = [];
227 
228  this.columns.forEach(function(column){
229  column.reRegisterPosition();
230  });
231 };
232 
233 //ensure column headers take up the correct amount of space in column groups
234 ColumnManager.prototype._verticalAlignHeaders = function(){
235  var self = this, minHeight = 0;
236 
237  self.columns.forEach(function(column){
238  var height;
239 
240  column.clearVerticalAlign();
241 
242  height = column.getHeight();
243 
244  if(height > minHeight){
245  minHeight = height;
246  }
247  });
248 
249  self.columns.forEach(function(column){
250  column.verticalAlign(self.table.options.columnHeaderVertAlign, minHeight);
251  });
252 
253  self.rowManager.adjustTableSize();
254 };
255 
257 
258 ColumnManager.prototype.findColumn = function(subject){
259  var self = this;
260 
261  if(typeof subject == "object"){
262 
263  if(subject instanceof Column){
264  //subject is column element
265  return subject;
266  }else if(subject instanceof ColumnComponent){
267  //subject is public column component
268  return subject._getSelf() || false;
269  }else if(typeof HTMLElement !== "undefined" && subject instanceof HTMLElement){
270  //subject is a HTML element of the column header
271  let match = self.columns.find(function(column){
272  return column.element === subject;
273  });
274 
275  return match || false;
276  }
277 
278  }else{
279  //subject should be treated as the field name of the column
280  return this.columnsByField[subject] || false;
281  }
282 
283  //catch all for any other type of input
284 
285  return false;
286 };
287 
288 ColumnManager.prototype.getColumnByField = function(field){
289  return this.columnsByField[field];
290 };
291 
292 
293 ColumnManager.prototype.getColumnsByFieldRoot = function(root){
294 
295  var matches = [];
296 
297  Object.keys(this.columnsByField).forEach((field) => {
298  var fieldRoot = field.split(".")[0];
299  if(fieldRoot === root){
300  matches.push(this.columnsByField[field]);
301  }
302  });
303 
304  return matches;
305 };
306 
307 ColumnManager.prototype.getColumnByIndex = function(index){
308  return this.columnsByIndex[index];
309 };
310 
311 ColumnManager.prototype.getFirstVisibileColumn = function(index){
312  var index = this.columnsByIndex.findIndex(function(col){
313  return col.visible;
314  });
315 
316  return index > -1 ? this.columnsByIndex[index] : false;
317 };
318 
319 ColumnManager.prototype.getColumns = function(){
320  return this.columns;
321 };
322 
323 ColumnManager.prototype.findColumnIndex = function(column){
324  return this.columnsByIndex.findIndex(function(col){
325  return column === col;
326  });
327 };
328 
329 //return all columns that are not groups
330 ColumnManager.prototype.getRealColumns = function(){
331  return this.columnsByIndex;
332 };
333 
334 //travers across columns and call action
335 ColumnManager.prototype.traverse = function(callback){
336  var self = this;
337 
338  self.columnsByIndex.forEach(function(column,i){
339  callback(column, i);
340  });
341 };
342 
343 //get defintions of actual columns
344 ColumnManager.prototype.getDefinitions = function(active){
345  var self = this,
346  output = [];
347 
348  self.columnsByIndex.forEach(function(column){
349  if(!active || (active && column.visible)){
350  output.push(column.getDefinition());
351  }
352  });
353 
354  return output;
355 };
356 
357 //get full nested definition tree
358 ColumnManager.prototype.getDefinitionTree = function(){
359  var self = this,
360  output = [];
361 
362  self.columns.forEach(function(column){
363  output.push(column.getDefinition(true));
364  });
365 
366  return output;
367 };
368 
369 ColumnManager.prototype.getComponents = function(structured){
370  var self = this,
371  output = [],
372  columns = structured ? self.columns : self.columnsByIndex;
373 
374  columns.forEach(function(column){
375  output.push(column.getComponent());
376  });
377 
378  return output;
379 };
380 
381 ColumnManager.prototype.getWidth = function(){
382  var width = 0;
383 
384  this.columnsByIndex.forEach(function(column){
385  if(column.visible){
386  width += column.getWidth();
387  }
388  });
389 
390  return width;
391 };
392 
393 
394 ColumnManager.prototype.moveColumn = function(from, to, after){
395  this.moveColumnActual(from, to, after);
396 
397  if(this.table.options.responsiveLayout && this.table.modExists("responsiveLayout", true)){
398  this.table.modules.responsiveLayout.initialize();
399  }
400 
401  if(this.table.modExists("columnCalcs")){
402  this.table.modules.columnCalcs.recalc(this.table.rowManager.activeRows);
403  }
404 
405  to.element.parentNode.insertBefore(from.element, to.element);
406 
407  if(after){
408  to.element.parentNode.insertBefore(to.element, from.element);
409  }
410 
411  this._verticalAlignHeaders();
412 
413  this.table.rowManager.reinitialize();
414 }
415 
416 ColumnManager.prototype.moveColumnActual = function(from, to, after){
417 
418  this._moveColumnInArray(this.columns, from, to, after);
419  this._moveColumnInArray(this.columnsByIndex, from, to, after, true);
420 
421  if(this.table.options.responsiveLayout && this.table.modExists("responsiveLayout", true)){
422  this.table.modules.responsiveLayout.initialize();
423  }
424 
425  if(this.table.options.columnMoved){
426  this.table.options.columnMoved.call(this.table, from.getComponent(), this.table.columnManager.getComponents());
427  }
428 
429  if(this.table.options.persistence && this.table.modExists("persistence", true) && this.table.modules.persistence.config.columns){
430  this.table.modules.persistence.save("columns");
431  }
432 };
433 
434 ColumnManager.prototype._moveColumnInArray = function(columns, from, to, after, updateRows){
435  var fromIndex = columns.indexOf(from),
436  toIndex;
437 
438  if (fromIndex > -1) {
439 
440  columns.splice(fromIndex, 1);
441 
442  toIndex = columns.indexOf(to);
443 
444  if (toIndex > -1) {
445 
446  if(after){
447  toIndex = toIndex+1;
448  }
449 
450  }else{
451  toIndex = fromIndex;
452  }
453 
454  columns.splice(toIndex, 0, from);
455 
456  if(updateRows){
457 
458  this.table.rowManager.rows.forEach(function(row){
459  if(row.cells.length){
460  var cell = row.cells.splice(fromIndex, 1)[0];
461  row.cells.splice(toIndex, 0, cell);
462  }
463  });
464  }
465  }
466 };
467 
468 ColumnManager.prototype.scrollToColumn = function(column, position, ifVisible){
469  var left = 0,
470  offset = 0,
471  adjust = 0,
472  colEl = column.getElement();
473 
474  return new Promise((resolve, reject) => {
475 
476  if(typeof position === "undefined"){
477  position = this.table.options.scrollToColumnPosition;
478  }
479 
480  if(typeof ifVisible === "undefined"){
481  ifVisible = this.table.options.scrollToColumnIfVisible;
482  }
483 
484  if(column.visible){
485 
486  //align to correct position
487  switch(position){
488  case "middle":
489  case "center":
490  adjust = -this.element.clientWidth / 2;
491  break;
492 
493  case "right":
494  adjust = colEl.clientWidth - this.headersElement.clientWidth;
495  break;
496  }
497 
498  //check column visibility
499  if(!ifVisible){
500 
501  offset = colEl.offsetLeft;
502 
503  if(offset > 0 && offset + colEl.offsetWidth < this.element.clientWidth){
504  return false;
505  }
506  }
507 
508  //calculate scroll position
509  left = colEl.offsetLeft + this.element.scrollLeft + adjust;
510 
511  left = Math.max(Math.min(left, this.table.rowManager.element.scrollWidth - this.table.rowManager.element.clientWidth),0);
512 
513  this.table.rowManager.scrollHorizontal(left);
514  this.scrollHorizontal(left);
515 
516  resolve();
517  }else{
518  console.warn("Scroll Error - Column not visible");
519  reject("Scroll Error - Column not visible");
520  }
521 
522  });
523 };
524 
526 
527 ColumnManager.prototype.generateCells = function(row){
528  var self = this;
529 
530  var cells = [];
531 
532  self.columnsByIndex.forEach(function(column){
533  cells.push(column.generateCell(row));
534  });
535 
536  return cells;
537 };
538 
540 
541 
542 ColumnManager.prototype.getFlexBaseWidth = function(){
543  var self = this,
544  totalWidth = self.table.element.clientWidth, //table element width
545  fixedWidth = 0;
546 
547  //adjust for vertical scrollbar if present
548  if(self.rowManager.element.scrollHeight > self.rowManager.element.clientHeight){
549  totalWidth -= self.rowManager.element.offsetWidth - self.rowManager.element.clientWidth;
550  }
551 
552  this.columnsByIndex.forEach(function(column){
553  var width, minWidth, colWidth;
554 
555  if(column.visible){
556 
557  width = column.definition.width || 0;
558 
559  minWidth = typeof column.minWidth == "undefined" ? self.table.options.columnMinWidth : parseInt(column.minWidth);
560 
561  if(typeof(width) == "string"){
562  if(width.indexOf("%") > -1){
563  colWidth = (totalWidth / 100) * parseInt(width) ;
564  }else{
565  colWidth = parseInt(width);
566  }
567  }else{
568  colWidth = width;
569  }
570 
571  fixedWidth += colWidth > minWidth ? colWidth : minWidth;
572 
573  }
574  });
575 
576  return fixedWidth;
577 };
578 
579 ColumnManager.prototype.addColumn = function(definition, before, nextToColumn){
580  return new Promise((resolve, reject) => {
581  var column = this._addColumn(definition, before, nextToColumn);
582 
583  this._reIndexColumns();
584 
585  if(this.table.options.responsiveLayout && this.table.modExists("responsiveLayout", true)){
586  this.table.modules.responsiveLayout.initialize();
587  }
588 
589  if(this.table.modExists("columnCalcs")){
590  this.table.modules.columnCalcs.recalc(this.table.rowManager.activeRows);
591  }
592 
593  this.redraw();
594 
595  if(this.table.modules.layout.getMode() != "fitColumns"){
596  column.reinitializeWidth();
597  }
598 
599  this._verticalAlignHeaders();
600 
601  this.table.rowManager.reinitialize();
602 
603  resolve(column);
604  });
605 };
606 
607 //remove column from system
608 ColumnManager.prototype.deregisterColumn = function(column){
609  var field = column.getField(),
610  index;
611 
612  //remove from field list
613  if(field){
614  delete this.columnsByField[field];
615  }
616 
617  //remove from index list
618  index = this.columnsByIndex.indexOf(column);
619 
620  if(index > -1){
621  this.columnsByIndex.splice(index, 1);
622  }
623 
624  //remove from column list
625  index = this.columns.indexOf(column);
626 
627  if(index > -1){
628  this.columns.splice(index, 1);
629  }
630 
631  if(this.table.options.responsiveLayout && this.table.modExists("responsiveLayout", true)){
632  this.table.modules.responsiveLayout.initialize();
633  }
634 
635  this.redraw();
636 };
637 
638 //redraw columns
639 ColumnManager.prototype.redraw = function(force){
640  if(force){
641 
642  if(Tabulator.prototype.helpers.elVisible(this.element)){
643  this._verticalAlignHeaders();
644  }
645 
646  this.table.rowManager.resetScroll();
647  this.table.rowManager.reinitialize();
648  }
649 
650  if(["fitColumns", "fitDataStretch"].indexOf(this.table.modules.layout.getMode()) > -1){
651  this.table.modules.layout.layout();
652  }else{
653  if(force){
654  this.table.modules.layout.layout();
655  }else{
656  if(this.table.options.responsiveLayout && this.table.modExists("responsiveLayout", true)){
657  this.table.modules.responsiveLayout.update();
658  }
659  }
660  }
661 
662  if(this.table.modExists("frozenColumns")){
663  this.table.modules.frozenColumns.layout();
664  }
665 
666  if(this.table.modExists("columnCalcs")){
667  this.table.modules.columnCalcs.recalc(this.table.rowManager.activeRows);
668  }
669 
670  if(force){
671  if(this.table.options.persistence && this.table.modExists("persistence", true) && this.table.modules.persistence.config.columns){
672  this.table.modules.persistence.save("columns");
673  }
674 
675  if(this.table.modExists("columnCalcs")){
676  this.table.modules.columnCalcs.redraw();
677  }
678  }
679 
680  this.table.footerManager.redraw();
681 
682 
683 
684 };