otsdaq_utilities  v2_05_02_indev
row_manager.js
1 var RowManager = function(table){
2 
3  this.table = table;
4  this.element = this.createHolderElement(); //containing element
5  this.tableElement = this.createTableElement(); //table element
6  this.columnManager = null; //hold column manager object
7  this.height = 0; //hold height of table element
8 
9  this.firstRender = false; //handle first render
10  this.renderMode = "classic"; //current rendering mode
11 
12  this.rows = []; //hold row data objects
13  this.activeRows = []; //rows currently available to on display in the table
14  this.activeRowsCount = 0; //count of active rows
15 
16  this.displayRows = []; //rows currently on display in the table
17  this.displayRowsCount = 0; //count of display rows
18 
19  this.scrollTop = 0;
20  this.scrollLeft = 0;
21 
22  this.vDomRowHeight = 20; //approximation of row heights for padding
23 
24  this.vDomTop = 0; //hold position for first rendered row in the virtual DOM
25  this.vDomBottom = 0; //hold possition for last rendered row in the virtual DOM
26 
27  this.vDomScrollPosTop = 0; //last scroll position of the vDom top;
28  this.vDomScrollPosBottom = 0; //last scroll position of the vDom bottom;
29 
30  this.vDomTopPad = 0; //hold value of padding for top of virtual DOM
31  this.vDomBottomPad = 0; //hold value of padding for bottom of virtual DOM
32 
33  this.vDomMaxRenderChain = 90; //the maximum number of dom elements that can be rendered in 1 go
34 
35  this.vDomWindowBuffer = 0; //window row buffer before removing elements, to smooth scrolling
36 
37  this.vDomWindowMinTotalRows = 20; //minimum number of rows to be generated in virtual dom (prevent buffering issues on tables with tall rows)
38  this.vDomWindowMinMarginRows = 5; //minimum number of rows to be generated in virtual dom margin
39 
40  this.vDomTopNewRows = []; //rows to normalize after appending to optimize render speed
41  this.vDomBottomNewRows = []; //rows to normalize after appending to optimize render speed
42 
43  this.rowNumColumn = false; //hold column component for row number column
44 
45  this.redrawBlock = false; //prevent redraws to allow multiple data manipulations becore continuing
46  this.redrawBlockRestoreConfig = false; //store latest redraw function calls for when redraw is needed
47  this.redrawBlockRederInPosition = false; //store latest redraw function calls for when redraw is needed
48 };
49 
51 
52 RowManager.prototype.createHolderElement = function (){
53  var el = document.createElement("div");
54 
55  el.classList.add("tabulator-tableHolder");
56  el.setAttribute("tabindex", 0);
57 
58  return el;
59 };
60 
61 RowManager.prototype.createTableElement = function (){
62  var el = document.createElement("div");
63 
64  el.classList.add("tabulator-table");
65 
66  return el;
67 };
68 
69 //return containing element
70 RowManager.prototype.getElement = function(){
71  return this.element;
72 };
73 
74 //return table element
75 RowManager.prototype.getTableElement = function(){
76  return this.tableElement;
77 };
78 
79 //return position of row in table
80 RowManager.prototype.getRowPosition = function(row, active){
81  if(active){
82  return this.activeRows.indexOf(row);
83  }else{
84  return this.rows.indexOf(row);
85  }
86 };
87 
88 
89 //link to column manager
90 RowManager.prototype.setColumnManager = function(manager){
91  this.columnManager = manager;
92 };
93 
94 RowManager.prototype.initialize = function(){
95  var self = this;
96 
97  self.setRenderMode();
98 
99  //initialize manager
100  self.element.appendChild(self.tableElement);
101 
102  self.firstRender = true;
103 
104  //scroll header along with table body
105  self.element.addEventListener("scroll", function(){
106  var left = self.element.scrollLeft;
107 
108  //handle horizontal scrolling
109  if(self.scrollLeft != left){
110  self.columnManager.scrollHorizontal(left);
111 
112  if(self.table.options.groupBy){
113  self.table.modules.groupRows.scrollHeaders(left);
114  }
115 
116  if(self.table.modExists("columnCalcs")){
117  self.table.modules.columnCalcs.scrollHorizontal(left);
118  }
119 
120  self.table.options.scrollHorizontal(left);
121  }
122 
123  self.scrollLeft = left;
124  });
125 
126  //handle virtual dom scrolling
127  if(this.renderMode === "virtual"){
128 
129  self.element.addEventListener("scroll", function(){
130  var top = self.element.scrollTop;
131  var dir = self.scrollTop > top;
132 
133  //handle verical scrolling
134  if(self.scrollTop != top){
135  self.scrollTop = top;
136  self.scrollVertical(dir);
137 
138  if(self.table.options.ajaxProgressiveLoad == "scroll"){
139  self.table.modules.ajax.nextPage(self.element.scrollHeight - self.element.clientHeight - top);
140  }
141 
142  self.table.options.scrollVertical(top);
143  }else{
144  self.scrollTop = top;
145  }
146 
147  });
148  }
149 };
150 
151 
153 
154 RowManager.prototype.findRow = function(subject){
155  var self = this;
156 
157  if(typeof subject == "object"){
158 
159  if(subject instanceof Row){
160  //subject is row element
161  return subject;
162  }else if(subject instanceof RowComponent){
163  //subject is public row component
164  return subject._getSelf() || false;
165  }else if(typeof HTMLElement !== "undefined" && subject instanceof HTMLElement){
166  //subject is a HTML element of the row
167  let match = self.rows.find(function(row){
168  return row.element === subject;
169  });
170 
171  return match || false;
172  }
173 
174  }else if(typeof subject == "undefined" || subject === null){
175  return false;
176  }else{
177  //subject should be treated as the index of the row
178  let match = self.rows.find(function(row){
179  return row.data[self.table.options.index] == subject;
180  });
181 
182  return match || false;
183  }
184 
185  //catch all for any other type of input
186 
187  return false;
188 };
189 
190 
191 RowManager.prototype.getRowFromDataObject = function(data){
192  var match = this.rows.find(function(row){
193  return row.data === data;
194  });
195 
196  return match || false;
197 };
198 
199 RowManager.prototype.getRowFromPosition = function(position, active){
200  if(active){
201  return this.activeRows[position];
202  }else{
203  return this.rows[position];
204  }
205 };
206 
207 RowManager.prototype.scrollToRow = function(row, position, ifVisible){
208  var rowIndex = this.getDisplayRows().indexOf(row),
209  rowEl = row.getElement(),
210  rowTop,
211  offset = 0;
212 
213  return new Promise((resolve, reject) => {
214  if(rowIndex > -1){
215 
216  if(typeof position === "undefined"){
217  position = this.table.options.scrollToRowPosition;
218  }
219 
220  if(typeof ifVisible === "undefined"){
221  ifVisible = this.table.options.scrollToRowIfVisible;
222  }
223 
224 
225  if(position === "nearest"){
226  switch(this.renderMode){
227  case"classic":
228  rowTop = Tabulator.prototype.helpers.elOffset(rowEl).top;
229  position = Math.abs(this.element.scrollTop - rowTop) > Math.abs(this.element.scrollTop + this.element.clientHeight - rowTop) ? "bottom" : "top";
230  break;
231  case"virtual":
232  position = Math.abs(this.vDomTop - rowIndex) > Math.abs(this.vDomBottom - rowIndex) ? "bottom" : "top";
233  break;
234  }
235  }
236 
237  //check row visibility
238  if(!ifVisible){
239  if(Tabulator.prototype.helpers.elVisible(rowEl)){
240  offset = Tabulator.prototype.helpers.elOffset(rowEl).top - Tabulator.prototype.helpers.elOffset(this.element).top;
241 
242  if(offset > 0 && offset < this.element.clientHeight - rowEl.offsetHeight){
243  return false;
244  }
245  }
246  }
247 
248  //scroll to row
249  switch(this.renderMode){
250  case"classic":
251  this.element.scrollTop = Tabulator.prototype.helpers.elOffset(rowEl).top - Tabulator.prototype.helpers.elOffset(this.element).top + this.element.scrollTop;
252  break;
253  case"virtual":
254  this._virtualRenderFill(rowIndex, true);
255  break;
256  }
257 
258  //align to correct position
259  switch(position){
260  case "middle":
261  case "center":
262 
263  if(this.element.scrollHeight - this.element.scrollTop == this.element.clientHeight){
264  this.element.scrollTop = this.element.scrollTop + (rowEl.offsetTop - this.element.scrollTop) - ((this.element.scrollHeight - rowEl.offsetTop) / 2);
265  }else{
266  this.element.scrollTop = this.element.scrollTop - (this.element.clientHeight / 2);
267  }
268 
269  break;
270 
271  case "bottom":
272 
273  if(this.element.scrollHeight - this.element.scrollTop == this.element.clientHeight){
274  this.element.scrollTop = this.element.scrollTop - (this.element.scrollHeight - rowEl.offsetTop) + rowEl.offsetHeight;
275  }else{
276  this.element.scrollTop = this.element.scrollTop - this.element.clientHeight + rowEl.offsetHeight;
277  }
278 
279  break;
280  }
281 
282  resolve();
283 
284  }else{
285  console.warn("Scroll Error - Row not visible");
286  reject("Scroll Error - Row not visible");
287  }
288  });
289 };
290 
291 
293 
294 RowManager.prototype.setData = function(data, renderInPosition){
295  var self = this;
296 
297  return new Promise((resolve, reject)=>{
298  if(renderInPosition && this.getDisplayRows().length){
299  if(self.table.options.pagination){
300  self._setDataActual(data, true);
301  }else{
302  this.reRenderInPosition(function(){
303  self._setDataActual(data);
304  });
305  }
306  }else{
307  if(this.table.options.autoColumns){
308  this.table.columnManager.generateColumnsFromRowData(data);
309  }
310  this.resetScroll();
311  this._setDataActual(data);
312  }
313 
314  resolve();
315  });
316 };
317 
318 RowManager.prototype._setDataActual = function(data, renderInPosition){
319  var self = this;
320 
321  self.table.options.dataLoading.call(this.table, data);
322 
323  this._wipeElements();
324 
325  if(this.table.options.history && this.table.modExists("history")){
326  this.table.modules.history.clear();
327  }
328 
329  if(Array.isArray(data)){
330 
331  if(this.table.modExists("selectRow")){
332  this.table.modules.selectRow.clearSelectionData();
333  }
334 
335  if(this.table.options.reactiveData && this.table.modExists("reactiveData", true)){
336  this.table.modules.reactiveData.watchData(data);
337  }
338 
339  data.forEach(function(def, i){
340  if(def && typeof def === "object"){
341  var row = new Row(def, self);
342  self.rows.push(row);
343  }else{
344  console.warn("Data Loading Warning - Invalid row data detected and ignored, expecting object but received:", def);
345  }
346  });
347 
348  self.table.options.dataLoaded.call(this.table, data);
349 
350  self.refreshActiveData(false, false, renderInPosition);
351  }else{
352  console.error("Data Loading Error - Unable to process data due to invalid data type \nExpecting: array \nReceived: ", typeof data, "\nData: ", data);
353  }
354 };
355 
356 RowManager.prototype._wipeElements = function(){
357  this.rows.forEach(function(row){
358  row.wipe();
359  });
360 
361  if(this.table.options.groupBy && this.table.modExists("groupRows")){
362  this.table.modules.groupRows.wipe();
363  }
364 
365  this.rows = [];
366 }
367 
368 RowManager.prototype.deleteRow = function(row, blockRedraw){
369  var allIndex = this.rows.indexOf(row),
370  activeIndex = this.activeRows.indexOf(row);
371 
372  if(activeIndex > -1){
373  this.activeRows.splice(activeIndex, 1);
374  }
375 
376  if(allIndex > -1){
377  this.rows.splice(allIndex, 1);
378  }
379 
380  this.setActiveRows(this.activeRows);
381 
382  this.displayRowIterator(function(rows){
383  var displayIndex = rows.indexOf(row);
384 
385  if(displayIndex > -1){
386  rows.splice(displayIndex, 1);
387  }
388  });
389 
390  if(!blockRedraw){
391  this.reRenderInPosition();
392  }
393 
394  this.table.options.rowDeleted.call(this.table, row.getComponent());
395 
396  this.table.options.dataEdited.call(this.table, this.getData());
397 
398  if(this.table.options.groupBy && this.table.modExists("groupRows")){
399  this.table.modules.groupRows.updateGroupRows(true);
400  }else if(this.table.options.pagination && this.table.modExists("page")){
401  this.refreshActiveData(false, false, true);
402  }else{
403  if(this.table.options.pagination && this.table.modExists("page")){
404  this.refreshActiveData("page");
405  }
406  }
407 
408 };
409 
410 RowManager.prototype.addRow = function(data, pos, index, blockRedraw){
411 
412  var row = this.addRowActual(data, pos, index, blockRedraw);
413 
414  if(this.table.options.history && this.table.modExists("history")){
415  this.table.modules.history.action("rowAdd", row, {data:data, pos:pos, index:index});
416  }
417 
418  return row;
419 };
420 
421 //add multiple rows
422 RowManager.prototype.addRows = function(data, pos, index){
423  var self = this,
424  length = 0,
425  rows = [];
426 
427  return new Promise((resolve, reject) => {
428  pos = this.findAddRowPos(pos);
429 
430  if(!Array.isArray(data)){
431  data = [data];
432  }
433 
434  length = data.length - 1;
435 
436  if((typeof index == "undefined" && pos) || (typeof index !== "undefined" && !pos)){
437  data.reverse();
438  }
439 
440  data.forEach(function(item, i){
441  var row = self.addRow(item, pos, index, true);
442  rows.push(row);
443  });
444 
445  if(this.table.options.groupBy && this.table.modExists("groupRows")){
446  this.table.modules.groupRows.updateGroupRows(true);
447  }else if(this.table.options.pagination && this.table.modExists("page")){
448  this.refreshActiveData(false, false, true);
449  }else{
450  this.reRenderInPosition();
451  }
452 
453  //recalc column calculations if present
454  if(this.table.modExists("columnCalcs")){
455  this.table.modules.columnCalcs.recalc(this.table.rowManager.activeRows);
456  }
457 
458  resolve(rows);
459  });
460 };
461 
462 RowManager.prototype.findAddRowPos = function(pos){
463  if(typeof pos === "undefined"){
464  pos = this.table.options.addRowPos;
465  }
466 
467  if(pos === "pos"){
468  pos = true;
469  }
470 
471  if(pos === "bottom"){
472  pos = false;
473  }
474 
475  return pos;
476 };
477 
478 
479 RowManager.prototype.addRowActual = function(data, pos, index, blockRedraw){
480  var row = data instanceof Row ? data : new Row(data || {}, this),
481  top = this.findAddRowPos(pos),
482  dispRows;
483 
484  if(!index && this.table.options.pagination && this.table.options.paginationAddRow == "page"){
485  dispRows = this.getDisplayRows();
486 
487  if(top){
488  if(dispRows.length){
489  index = dispRows[0];
490  }else{
491  if(this.activeRows.length){
492  index = this.activeRows[this.activeRows.length-1];
493  top = false;
494  }
495  }
496  }else{
497  if(dispRows.length){
498  index = dispRows[dispRows.length - 1];
499  top = dispRows.length < this.table.modules.page.getPageSize() ? false : true;
500  }
501  }
502  }
503 
504  if(index){
505  index = this.findRow(index);
506  }
507 
508  if(this.table.options.groupBy && this.table.modExists("groupRows")){
509  this.table.modules.groupRows.assignRowToGroup(row);
510 
511  var groupRows = row.getGroup().rows;
512 
513  if(groupRows.length > 1){
514 
515  if(!index || (index && groupRows.indexOf(index) == -1)){
516  if(top){
517  if(groupRows[0] !== row){
518  index = groupRows[0];
519  this._moveRowInArray(row.getGroup().rows, row, index, !top);
520  }
521  }else{
522  if(groupRows[groupRows.length -1] !== row){
523  index = groupRows[groupRows.length -1];
524  this._moveRowInArray(row.getGroup().rows, row, index, !top);
525  }
526  }
527  }else{
528  this._moveRowInArray(row.getGroup().rows, row, index, !top);
529  }
530  }
531  }
532 
533  if(index){
534  let allIndex = this.rows.indexOf(index),
535  activeIndex = this.activeRows.indexOf(index);
536 
537  this.displayRowIterator(function(rows){
538  var displayIndex = rows.indexOf(index);
539 
540  if(displayIndex > -1){
541  rows.splice((top ? displayIndex : displayIndex + 1), 0, row);
542  }
543  });
544 
545  if(activeIndex > -1){
546  this.activeRows.splice((top ? activeIndex : activeIndex + 1), 0, row);
547  }
548 
549  if(allIndex > -1){
550  this.rows.splice((top ? allIndex : allIndex + 1), 0, row);
551  }
552 
553  }else{
554 
555  if(top){
556 
557  this.displayRowIterator(function(rows){
558  rows.unshift(row);
559  });
560 
561  this.activeRows.unshift(row);
562  this.rows.unshift(row);
563  }else{
564  this.displayRowIterator(function(rows){
565  rows.push(row);
566  });
567 
568  this.activeRows.push(row);
569  this.rows.push(row);
570  }
571  }
572 
573  this.setActiveRows(this.activeRows);
574 
575  this.table.options.rowAdded.call(this.table, row.getComponent());
576 
577  this.table.options.dataEdited.call(this.table, this.getData());
578 
579  if(!blockRedraw){
580  this.reRenderInPosition();
581  }
582 
583  return row;
584 };
585 
586 RowManager.prototype.moveRow = function(from, to, after){
587  if(this.table.options.history && this.table.modExists("history")){
588  this.table.modules.history.action("rowMove", from, {pos:this.getRowPosition(from), to:to, after:after});
589  }
590 
591  this.moveRowActual(from, to, after);
592 
593  this.table.options.rowMoved.call(this.table, from.getComponent());
594 };
595 
596 
597 RowManager.prototype.moveRowActual = function(from, to, after){
598  var self = this;
599  this._moveRowInArray(this.rows, from, to, after);
600  this._moveRowInArray(this.activeRows, from, to, after);
601 
602  this.displayRowIterator(function(rows){
603  self._moveRowInArray(rows, from, to, after);
604  });
605 
606  if(this.table.options.groupBy && this.table.modExists("groupRows")){
607  var toGroup = to.getGroup();
608  var fromGroup = from.getGroup();
609 
610  if(toGroup === fromGroup){
611  this._moveRowInArray(toGroup.rows, from, to, after);
612  }else{
613  if(fromGroup){
614  fromGroup.removeRow(from);
615  }
616 
617  toGroup.insertRow(from, to, after);
618  }
619  }
620 };
621 
622 
623 RowManager.prototype._moveRowInArray = function(rows, from, to, after){
624  var fromIndex, toIndex, start, end;
625 
626  if(from !== to){
627 
628  fromIndex = rows.indexOf(from);
629 
630  if (fromIndex > -1) {
631 
632  rows.splice(fromIndex, 1);
633 
634  toIndex = rows.indexOf(to);
635 
636  if (toIndex > -1) {
637 
638  if(after){
639  rows.splice(toIndex+1, 0, from);
640  }else{
641  rows.splice(toIndex, 0, from);
642  }
643 
644  }else{
645  rows.splice(fromIndex, 0, from);
646  }
647  }
648 
649  //restyle rows
650  if(rows === this.getDisplayRows()){
651 
652  start = fromIndex < toIndex ? fromIndex : toIndex;
653  end = toIndex > fromIndex ? toIndex : fromIndex +1;
654 
655  for(let i = start; i <= end; i++){
656  if(rows[i]){
657  this.styleRow(rows[i], i);
658  }
659  }
660  }
661  }
662 };
663 
664 RowManager.prototype.clearData = function(){
665  this.setData([]);
666 };
667 
668 RowManager.prototype.getRowIndex = function(row){
669  return this.findRowIndex(row, this.rows);
670 };
671 
672 
673 RowManager.prototype.getDisplayRowIndex = function(row){
674  var index = this.getDisplayRows().indexOf(row);
675  return index > -1 ? index : false;
676 };
677 
678 RowManager.prototype.nextDisplayRow = function(row, rowOnly){
679  var index = this.getDisplayRowIndex(row),
680  nextRow = false;
681 
682 
683  if(index !== false && index < this.displayRowsCount -1){
684  nextRow = this.getDisplayRows()[index+1];
685  }
686 
687  if(nextRow && (!(nextRow instanceof Row) || nextRow.type != "row")){
688  return this.nextDisplayRow(nextRow, rowOnly);
689  }
690 
691  return nextRow;
692 };
693 
694 RowManager.prototype.prevDisplayRow = function(row, rowOnly){
695  var index = this.getDisplayRowIndex(row),
696  prevRow = false;
697 
698  if(index){
699  prevRow = this.getDisplayRows()[index-1];
700  }
701 
702  if(prevRow && (!(prevRow instanceof Row) || prevRow.type != "row")){
703  return this.prevDisplayRow(prevRow, rowOnly);
704  }
705 
706  return prevRow;
707 };
708 
709 RowManager.prototype.findRowIndex = function(row, list){
710  var rowIndex;
711 
712  row = this.findRow(row);
713 
714  if(row){
715  rowIndex = list.indexOf(row);
716 
717  if(rowIndex > -1){
718  return rowIndex;
719  }
720  }
721 
722  return false;
723 };
724 
725 
726 RowManager.prototype.getData = function(active, transform){
727  var output = [],
728  rows = this.getRows(active);
729 
730  rows.forEach(function(row){
731  output.push(row.getData(transform || "data"));
732  });
733 
734  return output;
735 };
736 
737 RowManager.prototype.getComponents = function(active){
738  var output = [],
739  rows = this.getRows(active);
740 
741  rows.forEach(function(row){
742  output.push(row.getComponent());
743  });
744 
745  return output;
746 };
747 
748 RowManager.prototype.getDataCount = function(active){
749  var rows = this.getRows(active);
750 
751  return rows.length;
752 };
753 
754 RowManager.prototype._genRemoteRequest = function(){
755  var self = this,
756  table = self.table,
757  options = table.options,
758  params = {};
759 
760  if(table.modExists("page")){
761  //set sort data if defined
762  if(options.ajaxSorting){
763  let sorters = self.table.modules.sort.getSort();
764 
765  sorters.forEach(function(item){
766  delete item.column;
767  });
768 
769  params[self.table.modules.page.paginationDataSentNames.sorters] = sorters;
770  }
771 
772  //set filter data if defined
773  if(options.ajaxFiltering){
774  let filters = self.table.modules.filter.getFilters(true, true);
775 
776  params[self.table.modules.page.paginationDataSentNames.filters] = filters;
777  }
778 
779 
780  self.table.modules.ajax.setParams(params, true);
781  }
782 
783  table.modules.ajax.sendRequest()
784  .then((data)=>{
785  self.setData(data);
786  })
787  .catch((e)=>{});
788 
789 };
790 
791 //choose the path to refresh data after a filter update
792 RowManager.prototype.filterRefresh = function(){
793  var table = this.table,
794  options = table.options,
795  left = this.scrollLeft;
796 
797 
798  if(options.ajaxFiltering){
799  if(options.pagination == "remote" && table.modExists("page")){
800  table.modules.page.reset(true);
801  table.modules.page.setPage(1).then(()=>{}).catch(()=>{});
802  }else if(options.ajaxProgressiveLoad){
803  table.modules.ajax.loadData().then(()=>{}).catch(()=>{});
804  }else{
805  //assume data is url, make ajax call to url to get data
806  this._genRemoteRequest();
807  }
808  }else{
809  this.refreshActiveData("filter");
810  }
811 
812  this.scrollHorizontal(left);
813 };
814 
815 //choose the path to refresh data after a sorter update
816 RowManager.prototype.sorterRefresh = function(loadOrignalData){
817  var table = this.table,
818  options = this.table.options,
819  left = this.scrollLeft;
820 
821  if(options.ajaxSorting){
822  if((options.pagination == "remote" || options.progressiveLoad) && table.modExists("page")){
823  table.modules.page.reset(true);
824  table.modules.page.setPage(1).then(()=>{}).catch(()=>{});
825  }else if(options.ajaxProgressiveLoad){
826  table.modules.ajax.loadData().then(()=>{}).catch(()=>{});
827  }else{
828  //assume data is url, make ajax call to url to get data
829  this._genRemoteRequest();
830  }
831  }else{
832  this.refreshActiveData(loadOrignalData ? "filter" : "sort");
833  }
834 
835  this.scrollHorizontal(left);
836 };
837 
838 RowManager.prototype.scrollHorizontal = function(left){
839  this.scrollLeft = left;
840  this.element.scrollLeft = left;
841 
842  if(this.table.options.groupBy){
843  this.table.modules.groupRows.scrollHeaders(left);
844  }
845 
846  if(this.table.modExists("columnCalcs")){
847  this.table.modules.columnCalcs.scrollHorizontal(left);
848  }
849 };
850 
851 //set active data set
852 RowManager.prototype.refreshActiveData = function(stage, skipStage, renderInPosition){
853  var self = this,
854  table = this.table,
855  cascadeOrder = ["all", "filter", "sort", "display", "freeze", "group", "tree", "page"],
856  displayIndex;
857 
858  if(this.redrawBlock){
859 
860  if(!this.redrawBlockRestoreConfig || (cascadeOrder.indexOf(stage) < cascadeOrder.indexOf(this.redrawBlockRestoreConfig.stage))){
861  this.redrawBlockRestoreConfig = {
862  stage: stage,
863  skipStage: skipStage,
864  renderInPosition: renderInPosition,
865  };
866  }
867 
868  return;
869  }else{
870 
871  if(self.table.modExists("edit")){
872  self.table.modules.edit.cancelEdit();
873  }
874 
875  if(!stage){
876  stage = "all";
877  }
878 
879  if(table.options.selectable && !table.options.selectablePersistence && table.modExists("selectRow")){
880  table.modules.selectRow.deselectRows();
881  }
882 
883  //cascade through data refresh stages
884  switch(stage){
885  case "all":
886 
887  case "filter":
888  if(!skipStage){
889  if(table.modExists("filter")){
890  self.setActiveRows(table.modules.filter.filter(self.rows));
891  }else{
892  self.setActiveRows(self.rows.slice(0));
893  }
894  }else{
895  skipStage = false;
896  }
897 
898  case "sort":
899  if(!skipStage){
900  if(table.modExists("sort")){
901  table.modules.sort.sort(this.activeRows);
902  }
903  }else{
904  skipStage = false;
905  }
906 
907  //regenerate row numbers for row number formatter if in use
908  if(this.rowNumColumn){
909  this.activeRows.forEach((row) => {
910  var cell = row.getCell(this.rowNumColumn);
911 
912  if(cell){
913  cell._generateContents();
914  }
915  });
916  }
917 
918  //generic stage to allow for pipeline trigger after the data manipulation stage
919  case "display":
920  this.resetDisplayRows();
921 
922  case "freeze":
923  if(!skipStage){
924  if(this.table.modExists("frozenRows")){
925  if(table.modules.frozenRows.isFrozen()){
926  if(!table.modules.frozenRows.getDisplayIndex()){
927  table.modules.frozenRows.setDisplayIndex(this.getNextDisplayIndex());
928  }
929 
930  displayIndex = table.modules.frozenRows.getDisplayIndex();
931 
932  displayIndex = self.setDisplayRows(table.modules.frozenRows.getRows(this.getDisplayRows(displayIndex - 1)), displayIndex);
933 
934  if(displayIndex !== true){
935  table.modules.frozenRows.setDisplayIndex(displayIndex);
936  }
937  }
938  }
939  }else{
940  skipStage = false;
941  }
942 
943  case "group":
944  if(!skipStage){
945  if(table.options.groupBy && table.modExists("groupRows")){
946 
947  if(!table.modules.groupRows.getDisplayIndex()){
948  table.modules.groupRows.setDisplayIndex(this.getNextDisplayIndex());
949  }
950 
951  displayIndex = table.modules.groupRows.getDisplayIndex();
952 
953  displayIndex = self.setDisplayRows(table.modules.groupRows.getRows(this.getDisplayRows(displayIndex - 1)), displayIndex);
954 
955  if(displayIndex !== true){
956  table.modules.groupRows.setDisplayIndex(displayIndex);
957  }
958  }
959  }else{
960  skipStage = false;
961  }
962 
963 
964 
965  case "tree":
966 
967  if(!skipStage){
968  if(table.options.dataTree && table.modExists("dataTree")){
969  if(!table.modules.dataTree.getDisplayIndex()){
970  table.modules.dataTree.setDisplayIndex(this.getNextDisplayIndex());
971  }
972 
973  displayIndex = table.modules.dataTree.getDisplayIndex();
974 
975  displayIndex = self.setDisplayRows(table.modules.dataTree.getRows(this.getDisplayRows(displayIndex - 1)), displayIndex);
976 
977  if(displayIndex !== true){
978  table.modules.dataTree.setDisplayIndex(displayIndex);
979  }
980  }
981  }else{
982  skipStage = false;
983  }
984 
985  if(table.options.pagination && table.modExists("page") && !renderInPosition){
986  if(table.modules.page.getMode() == "local"){
987  table.modules.page.reset();
988  }
989  }
990 
991  case "page":
992  if(!skipStage){
993  if(table.options.pagination && table.modExists("page")){
994 
995  if(!table.modules.page.getDisplayIndex()){
996  table.modules.page.setDisplayIndex(this.getNextDisplayIndex());
997  }
998 
999  displayIndex = table.modules.page.getDisplayIndex();
1000 
1001  if(table.modules.page.getMode() == "local"){
1002  table.modules.page.setMaxRows(this.getDisplayRows(displayIndex - 1).length);
1003  }
1004 
1005 
1006  displayIndex = self.setDisplayRows(table.modules.page.getRows(this.getDisplayRows(displayIndex - 1)), displayIndex);
1007 
1008  if(displayIndex !== true){
1009  table.modules.page.setDisplayIndex(displayIndex);
1010  }
1011  }
1012  }else{
1013  skipStage = false;
1014  }
1015  }
1016 
1017 
1018  if(Tabulator.prototype.helpers.elVisible(self.element)){
1019  if(renderInPosition){
1020  self.reRenderInPosition();
1021  }else{
1022  self.renderTable();
1023  if(table.options.layoutColumnsOnNewData){
1024  self.table.columnManager.redraw(true);
1025  }
1026  }
1027  }
1028 
1029  if(table.modExists("columnCalcs")){
1030  table.modules.columnCalcs.recalc(this.activeRows);
1031  }
1032  }
1033 };
1034 
1035 RowManager.prototype.setActiveRows = function(activeRows){
1036  this.activeRows = activeRows;
1037  this.activeRowsCount = this.activeRows.length;
1038 };
1039 
1040 //reset display rows array
1041 RowManager.prototype.resetDisplayRows = function(){
1042  this.displayRows = [];
1043 
1044  this.displayRows.push(this.activeRows.slice(0));
1045 
1046  this.displayRowsCount = this.displayRows[0].length;
1047 
1048  if(this.table.modExists("frozenRows")){
1049  this.table.modules.frozenRows.setDisplayIndex(0);
1050  }
1051 
1052  if(this.table.options.groupBy && this.table.modExists("groupRows")){
1053  this.table.modules.groupRows.setDisplayIndex(0);
1054  }
1055 
1056  if(this.table.options.pagination && this.table.modExists("page")){
1057  this.table.modules.page.setDisplayIndex(0);
1058  }
1059 };
1060 
1061 
1062 RowManager.prototype.getNextDisplayIndex = function(){
1063  return this.displayRows.length;
1064 };
1065 
1066 //set display row pipeline data
1067 RowManager.prototype.setDisplayRows = function(displayRows, index){
1068 
1069  var output = true;
1070 
1071  if(index && typeof this.displayRows[index] != "undefined"){
1072  this.displayRows[index] = displayRows;
1073  output = true;
1074  }else{
1075  this.displayRows.push(displayRows)
1076  output = index = this.displayRows.length -1;
1077  }
1078 
1079  if(index == this.displayRows.length -1){
1080  this.displayRowsCount = this.displayRows[this.displayRows.length -1].length;
1081  }
1082 
1083  return output;
1084 };
1085 
1086 RowManager.prototype.getDisplayRows = function(index){
1087  if(typeof index == "undefined"){
1088  return this.displayRows.length ? this.displayRows[this.displayRows.length -1] : [];
1089  }else{
1090  return this.displayRows[index] || [];
1091  }
1092 
1093 };
1094 
1095 
1096 RowManager.prototype.getVisibleRows = function(viewable){
1097  var topEdge = this.element.scrollTop,
1098  bottomEdge = this.element.clientHeight + topEdge,
1099  topFound = false,
1100  topRow = 0,
1101  bottomRow = 0,
1102  rows = this.getDisplayRows();
1103 
1104  if(viewable){
1105 
1106  this.getDisplayRows();
1107  for(var i = this.vDomTop; i <= this.vDomBottom; i++){
1108  if(rows[i]){
1109  if(!topFound){
1110  if((topEdge - rows[i].getElement().offsetTop) >= 0){
1111  topRow = i;
1112  }else{
1113  topFound = true;
1114 
1115  if(bottomEdge - rows[i].getElement().offsetTop >= 0){
1116  bottomRow = i;
1117  }else{
1118  break;
1119  }
1120  }
1121  }else{
1122  if(bottomEdge - rows[i].getElement().offsetTop >= 0){
1123  bottomRow = i;
1124  }else{
1125  break;
1126  }
1127  }
1128  }
1129  }
1130  }else{
1131  topRow = this.vDomTop;
1132  bottomRow = this.vDomBottom;
1133  }
1134 
1135  return rows.slice(topRow, bottomRow + 1);
1136 };
1137 
1138 
1139 //repeat action accross display rows
1140 RowManager.prototype.displayRowIterator = function(callback){
1141  this.displayRows.forEach(callback);
1142 
1143  this.displayRowsCount = this.displayRows[this.displayRows.length -1].length;
1144 };
1145 
1146 //return only actual rows (not group headers etc)
1147 RowManager.prototype.getRows = function(active){
1148  var rows;
1149 
1150  switch(active){
1151  case "active":
1152  rows = this.activeRows;
1153  break;
1154 
1155  case "visible":
1156  rows = this.getVisibleRows(true);
1157  break;
1158 
1159  default:
1160  rows = this.rows;
1161  }
1162 
1163  return rows;
1164 };
1165 
1167 
1168 //trigger rerender of table in current position
1169 RowManager.prototype.reRenderInPosition = function(callback){
1170  if(this.getRenderMode() == "virtual"){
1171 
1172  if(this.redrawBlock){
1173  if(callback){
1174  callback();
1175  }else{
1176  this.redrawBlockRederInPosition = true;
1177  }
1178  }else{
1179  var scrollTop = this.element.scrollTop;
1180  var topRow = false;
1181  var topOffset = false;
1182 
1183  var left = this.scrollLeft;
1184 
1185  var rows = this.getDisplayRows();
1186 
1187  for(var i = this.vDomTop; i <= this.vDomBottom; i++){
1188 
1189  if(rows[i]){
1190  var diff = scrollTop - rows[i].getElement().offsetTop;
1191 
1192  if(topOffset === false || Math.abs(diff) < topOffset){
1193  topOffset = diff;
1194  topRow = i;
1195  }else{
1196  break;
1197  }
1198  }
1199  }
1200 
1201  if(callback){
1202  callback();
1203  }
1204 
1205  this._virtualRenderFill((topRow === false ? this.displayRowsCount - 1 : topRow), true, topOffset || 0);
1206 
1207  this.scrollHorizontal(left);
1208  }
1209  }else{
1210  this.renderTable();
1211 
1212  if(callback){
1213  callback();
1214  }
1215  }
1216 };
1217 
1218 RowManager.prototype.setRenderMode = function(){
1219  if((this.table.element.clientHeight || this.table.options.height) && this.table.options.virtualDom){
1220  this.renderMode = "virtual";
1221  }else{
1222  this.renderMode = "classic";
1223  }
1224 };
1225 
1226 
1227 RowManager.prototype.getRenderMode = function(){
1228  return this.renderMode;
1229 };
1230 
1231 RowManager.prototype.renderTable = function(){
1232  var self = this;
1233 
1234  self.table.options.renderStarted.call(this.table);
1235 
1236  self.element.scrollTop = 0;
1237 
1238  switch(self.renderMode){
1239  case "classic":
1240  self._simpleRender();
1241  break;
1242 
1243  case "virtual":
1244  self._virtualRenderFill();
1245  break;
1246  }
1247 
1248  if(self.firstRender){
1249  if(self.displayRowsCount){
1250  self.firstRender = false;
1251  self.table.modules.layout.layout();
1252  }else{
1253  self.renderEmptyScroll();
1254  }
1255  }
1256 
1257  if(self.table.modExists("frozenColumns")){
1258  self.table.modules.frozenColumns.layout();
1259  }
1260 
1261 
1262  if(!self.displayRowsCount){
1263  if(self.table.options.placeholder){
1264 
1265  if(this.renderMode){
1266  self.table.options.placeholder.setAttribute("tabulator-render-mode", this.renderMode);
1267  }
1268 
1269  self.getElement().appendChild(self.table.options.placeholder);
1270  }
1271  }
1272 
1273  self.table.options.renderComplete.call(this.table);
1274 };
1275 
1276 //simple render on heightless table
1277 RowManager.prototype._simpleRender = function(){
1278  this._clearVirtualDom();
1279 
1280  if(this.displayRowsCount){
1281  this.checkClassicModeGroupHeaderWidth();
1282  }else{
1283  this.renderEmptyScroll();
1284  }
1285 };
1286 
1287 RowManager.prototype.checkClassicModeGroupHeaderWidth = function(){
1288  var self = this,
1289  element = this.tableElement,
1290  onlyGroupHeaders = true;
1291 
1292  self.getDisplayRows().forEach(function(row, index){
1293  self.styleRow(row, index);
1294  element.appendChild(row.getElement());
1295  row.initialize(true);
1296 
1297  if(row.type !== "group"){
1298  onlyGroupHeaders = false;
1299  }
1300  });
1301 
1302  if(onlyGroupHeaders){
1303  element.style.minWidth = self.table.columnManager.getWidth() + "px";
1304  }else{
1305  element.style.minWidth = "";
1306  }
1307 };
1308 
1309 //show scrollbars on empty table div
1310 RowManager.prototype.renderEmptyScroll = function(){
1311  this.tableElement.style.minWidth = this.table.columnManager.getWidth() + "px";
1312  this.tableElement.style.minHeight = "1px";
1313  this.tableElement.style.visibility = "hidden";
1314 };
1315 
1316 RowManager.prototype._clearVirtualDom = function(){
1317  var element = this.tableElement;
1318 
1319  if(this.table.options.placeholder && this.table.options.placeholder.parentNode){
1320  this.table.options.placeholder.parentNode.removeChild(this.table.options.placeholder);
1321  }
1322 
1323  // element.children.detach();
1324  while(element.firstChild) element.removeChild(element.firstChild);
1325 
1326  element.style.paddingTop = "";
1327  element.style.paddingBottom = "";
1328  element.style.minWidth = "";
1329  element.style.minHeight = "";
1330  element.style.visibility = "";
1331 
1332  this.scrollTop = 0;
1333  this.scrollLeft = 0;
1334  this.vDomTop = 0;
1335  this.vDomBottom = 0;
1336  this.vDomTopPad = 0;
1337  this.vDomBottomPad = 0;
1338 };
1339 
1340 RowManager.prototype.styleRow = function(row, index){
1341  var rowEl = row.getElement();
1342 
1343  if(index % 2){
1344  rowEl.classList.add("tabulator-row-even");
1345  rowEl.classList.remove("tabulator-row-odd");
1346  }else{
1347  rowEl.classList.add("tabulator-row-odd");
1348  rowEl.classList.remove("tabulator-row-even");
1349  }
1350 };
1351 
1352 //full virtual render
1353 RowManager.prototype._virtualRenderFill = function(position, forceMove, offset){
1354  var self = this,
1355  element = self.tableElement,
1356  holder = self.element,
1357  topPad = 0,
1358  rowsHeight = 0,
1359  topPadHeight = 0,
1360  i = 0,
1361  onlyGroupHeaders = true,
1362  rows = self.getDisplayRows();
1363 
1364  position = position || 0;
1365 
1366  offset = offset || 0;
1367 
1368  if(!position){
1369  self._clearVirtualDom();
1370  }else{
1371  while(element.firstChild) element.removeChild(element.firstChild);
1372 
1373  //check if position is too close to bottom of table
1374  let heightOccupied = (self.displayRowsCount - position + 1) * self.vDomRowHeight;
1375 
1376  if(heightOccupied < self.height){
1377  position -= Math.ceil((self.height - heightOccupied ) / self.vDomRowHeight);
1378 
1379  if(position < 0){
1380  position = 0;
1381  }
1382  }
1383 
1384  //calculate initial pad
1385  topPad = Math.min(Math.max(Math.floor(self.vDomWindowBuffer / self.vDomRowHeight), self.vDomWindowMinMarginRows), position);
1386  position -= topPad;
1387  }
1388 
1389  if(self.displayRowsCount && Tabulator.prototype.helpers.elVisible(self.element)){
1390 
1391  self.vDomTop = position;
1392 
1393  self.vDomBottom = position -1;
1394 
1395  while ((rowsHeight <= self.height + self.vDomWindowBuffer || i < self.vDomWindowMinTotalRows) && self.vDomBottom < self.displayRowsCount -1){
1396  var index = self.vDomBottom + 1,
1397  row = rows[index],
1398  rowHeight = 0;
1399 
1400  self.styleRow(row, index);
1401 
1402  element.appendChild(row.getElement());
1403  if(!row.initialized){
1404  row.initialize(true);
1405  }else{
1406  if(!row.heightInitialized){
1407  row.normalizeHeight(true);
1408  }
1409  }
1410 
1411  rowHeight = row.getHeight();
1412 
1413  if(i < topPad){
1414  topPadHeight += rowHeight;
1415  }else{
1416  rowsHeight += rowHeight;
1417  }
1418 
1419 
1420  if(rowHeight > this.vDomWindowBuffer){
1421  this.vDomWindowBuffer = rowHeight * 2;
1422  }
1423 
1424  if(row.type !== "group"){
1425  onlyGroupHeaders = false;
1426  }
1427 
1428  self.vDomBottom ++;
1429  i++;
1430  }
1431 
1432  if(!position){
1433  this.vDomTopPad = 0;
1434  //adjust rowheight to match average of rendered elements
1435  self.vDomRowHeight = Math.floor((rowsHeight + topPadHeight) / i);
1436  self.vDomBottomPad = self.vDomRowHeight * (self.displayRowsCount - self.vDomBottom -1);
1437 
1438  self.vDomScrollHeight = topPadHeight + rowsHeight + self.vDomBottomPad - self.height;
1439  }else{
1440  self.vDomTopPad = !forceMove ? self.scrollTop - topPadHeight : (self.vDomRowHeight * this.vDomTop) + offset;
1441  self.vDomBottomPad = self.vDomBottom == self.displayRowsCount-1 ? 0 : Math.max(self.vDomScrollHeight - self.vDomTopPad - rowsHeight - topPadHeight, 0);
1442  }
1443 
1444  element.style.paddingTop = self.vDomTopPad + "px";
1445  element.style.paddingBottom = self.vDomBottomPad + "px";
1446 
1447  if(forceMove){
1448  this.scrollTop = self.vDomTopPad + (topPadHeight) + offset - (this.element.scrollWidth > this.element.clientWidth ? this.element.offsetHeight - this.element.clientHeight : 0);
1449  }
1450 
1451  this.scrollTop = Math.min(this.scrollTop, this.element.scrollHeight - this.height);
1452 
1453  //adjust for horizontal scrollbar if present (and not at top of table)
1454  if(this.element.scrollWidth > this.element.offsetWidth && forceMove){
1455  this.scrollTop += this.element.offsetHeight - this.element.clientHeight;
1456  }
1457 
1458  this.vDomScrollPosTop = this.scrollTop;
1459  this.vDomScrollPosBottom = this.scrollTop;
1460 
1461  holder.scrollTop = this.scrollTop;
1462 
1463  element.style.minWidth = onlyGroupHeaders ? self.table.columnManager.getWidth() + "px" : "";
1464 
1465  if(self.table.options.groupBy){
1466  if(self.table.modules.layout.getMode() != "fitDataFill" && self.displayRowsCount == self.table.modules.groupRows.countGroups()){
1467  self.tableElement.style.minWidth = self.table.columnManager.getWidth();
1468  }
1469  }
1470 
1471  }else{
1472  this.renderEmptyScroll();
1473  }
1474 };
1475 
1476 //handle vertical scrolling
1477 RowManager.prototype.scrollVertical = function(dir){
1478  var topDiff = this.scrollTop - this.vDomScrollPosTop;
1479  var bottomDiff = this.scrollTop - this.vDomScrollPosBottom;
1480  var margin = this.vDomWindowBuffer * 2;
1481 
1482  if(-topDiff > margin || bottomDiff > margin){
1483  //if big scroll redraw table;
1484  var left = this.scrollLeft;
1485  this._virtualRenderFill(Math.floor((this.element.scrollTop / this.element.scrollHeight) * this.displayRowsCount));
1486  this.scrollHorizontal(left);
1487  }else{
1488 
1489  if(dir){
1490  //scrolling up
1491  if(topDiff < 0){
1492  this._addTopRow(-topDiff);
1493  }
1494 
1495  if(bottomDiff < 0){
1496 
1497  //hide bottom row if needed
1498  if(this.vDomScrollHeight - this.scrollTop > this.vDomWindowBuffer){
1499  this._removeBottomRow(-bottomDiff);
1500  }
1501  }
1502  }else{
1503  //scrolling down
1504  if(topDiff >= 0){
1505 
1506  //hide top row if needed
1507  if(this.scrollTop > this.vDomWindowBuffer){
1508  this._removeTopRow(topDiff);
1509  }
1510  }
1511 
1512  if(bottomDiff >= 0){
1513  this._addBottomRow(bottomDiff);
1514  }
1515  }
1516  }
1517 };
1518 
1519 RowManager.prototype._addTopRow = function(topDiff, i=0){
1520  var table = this.tableElement,
1521  rows = this.getDisplayRows();
1522 
1523  if(this.vDomTop){
1524  let index = this.vDomTop -1,
1525  topRow = rows[index],
1526  topRowHeight = topRow.getHeight() || this.vDomRowHeight;
1527 
1528  //hide top row if needed
1529  if(topDiff >= topRowHeight){
1530  this.styleRow(topRow, index);
1531  table.insertBefore(topRow.getElement(), table.firstChild);
1532  if(!topRow.initialized || !topRow.heightInitialized){
1533  this.vDomTopNewRows.push(topRow);
1534 
1535  if(!topRow.heightInitialized){
1536  topRow.clearCellHeight();
1537  }
1538  }
1539  topRow.initialize();
1540 
1541  this.vDomTopPad -= topRowHeight;
1542 
1543  if(this.vDomTopPad < 0){
1544  this.vDomTopPad = index * this.vDomRowHeight;
1545  }
1546 
1547  if(!index){
1548  this.vDomTopPad = 0;
1549  }
1550 
1551  table.style.paddingTop = this.vDomTopPad + "px";
1552  this.vDomScrollPosTop -= topRowHeight;
1553  this.vDomTop--;
1554  }
1555 
1556  topDiff = -(this.scrollTop - this.vDomScrollPosTop);
1557 
1558  if(topRow.getHeight() > this.vDomWindowBuffer){
1559  this.vDomWindowBuffer = topRow.getHeight() * 2;
1560  }
1561 
1562  if(i < this.vDomMaxRenderChain && this.vDomTop && topDiff >= (rows[this.vDomTop -1].getHeight() || this.vDomRowHeight)){
1563  this._addTopRow(topDiff, i+1);
1564  }else{
1565  this._quickNormalizeRowHeight(this.vDomTopNewRows);
1566  }
1567 
1568  }
1569 
1570 };
1571 
1572 RowManager.prototype._removeTopRow = function(topDiff){
1573  var table = this.tableElement,
1574  topRow = this.getDisplayRows()[this.vDomTop],
1575  topRowHeight = topRow.getHeight() || this.vDomRowHeight;
1576 
1577  if(topDiff >= topRowHeight){
1578 
1579  var rowEl = topRow.getElement();
1580  rowEl.parentNode.removeChild(rowEl);
1581 
1582  this.vDomTopPad += topRowHeight;
1583  table.style.paddingTop = this.vDomTopPad + "px";
1584  this.vDomScrollPosTop += this.vDomTop ? topRowHeight : topRowHeight + this.vDomWindowBuffer;
1585  this.vDomTop++;
1586 
1587  topDiff = this.scrollTop - this.vDomScrollPosTop;
1588 
1589  this._removeTopRow(topDiff);
1590  }
1591 
1592 };
1593 
1594 RowManager.prototype._addBottomRow = function(bottomDiff, i=0){
1595  var table = this.tableElement,
1596  rows = this.getDisplayRows();
1597 
1598  if(this.vDomBottom < this.displayRowsCount -1){
1599  let index = this.vDomBottom + 1,
1600  bottomRow = rows[index],
1601  bottomRowHeight = bottomRow.getHeight() || this.vDomRowHeight;
1602 
1603  //hide bottom row if needed
1604  if(bottomDiff >= bottomRowHeight){
1605  this.styleRow(bottomRow, index);
1606  table.appendChild(bottomRow.getElement());
1607 
1608  if(!bottomRow.initialized || !bottomRow.heightInitialized){
1609  this.vDomBottomNewRows.push(bottomRow);
1610 
1611  if(!bottomRow.heightInitialized){
1612  bottomRow.clearCellHeight();
1613  }
1614  }
1615 
1616  bottomRow.initialize();
1617 
1618  this.vDomBottomPad -= bottomRowHeight;
1619 
1620  if(this.vDomBottomPad < 0 || index == this.displayRowsCount -1){
1621  this.vDomBottomPad = 0;
1622  }
1623 
1624  table.style.paddingBottom = this.vDomBottomPad + "px";
1625  this.vDomScrollPosBottom += bottomRowHeight;
1626  this.vDomBottom++;
1627  }
1628 
1629  bottomDiff = this.scrollTop - this.vDomScrollPosBottom;
1630 
1631  if(bottomRow.getHeight() > this.vDomWindowBuffer){
1632  this.vDomWindowBuffer = bottomRow.getHeight() * 2;
1633  }
1634 
1635  if(i < this.vDomMaxRenderChain && this.vDomBottom < this.displayRowsCount -1 && bottomDiff >= (rows[this.vDomBottom + 1].getHeight() || this.vDomRowHeight)){
1636  this._addBottomRow(bottomDiff, i+1);
1637  }else{
1638  this._quickNormalizeRowHeight(this.vDomBottomNewRows);
1639  }
1640  }
1641 };
1642 
1643 RowManager.prototype._removeBottomRow = function(bottomDiff){
1644  var table = this.tableElement,
1645  bottomRow = this.getDisplayRows()[this.vDomBottom],
1646  bottomRowHeight = bottomRow.getHeight() || this.vDomRowHeight;
1647 
1648  if(bottomDiff >= bottomRowHeight){
1649 
1650  var rowEl = bottomRow.getElement();
1651 
1652  if(rowEl.parentNode){
1653  rowEl.parentNode.removeChild(rowEl);
1654  }
1655 
1656  this.vDomBottomPad += bottomRowHeight;
1657 
1658  if(this.vDomBottomPad < 0){
1659  this.vDomBottomPad = 0;
1660  }
1661 
1662  table.style.paddingBottom = this.vDomBottomPad + "px";
1663  this.vDomScrollPosBottom -= bottomRowHeight;
1664  this.vDomBottom--;
1665 
1666  bottomDiff = -(this.scrollTop - this.vDomScrollPosBottom);
1667 
1668  this._removeBottomRow(bottomDiff);
1669  }
1670 };
1671 
1672 RowManager.prototype._quickNormalizeRowHeight = function(rows){
1673  rows.forEach(function(row){
1674  row.calcHeight();
1675  });
1676 
1677  rows.forEach(function(row){
1678  row.setCellHeight();
1679  });
1680 
1681  rows.length = 0;
1682 };
1683 
1684 //normalize height of active rows
1685 RowManager.prototype.normalizeHeight = function(){
1686  this.activeRows.forEach(function(row){
1687  row.normalizeHeight();
1688  });
1689 };
1690 
1691 //adjust the height of the table holder to fit in the Tabulator element
1692 RowManager.prototype.adjustTableSize = function(){
1693 
1694  if(this.renderMode === "virtual"){
1695  this.height = this.element.clientHeight;
1696  this.vDomWindowBuffer = this.table.options.virtualDomBuffer || this.height;
1697 
1698  let otherHeight = this.columnManager.getElement().offsetHeight + (this.table.footerManager && !this.table.footerManager.external ? this.table.footerManager.getElement().offsetHeight : 0);
1699 
1700  this.element.style.minHeight = "calc(100% - " + otherHeight + "px)";
1701  this.element.style.height = "calc(100% - " + otherHeight + "px)";
1702  this.element.style.maxHeight = "calc(100% - " + otherHeight + "px)";
1703  }
1704 };
1705 
1706 //renitialize all rows
1707 RowManager.prototype.reinitialize = function(){
1708  this.rows.forEach(function(row){
1709  row.reinitialize();
1710  });
1711 };
1712 
1713 //prevent table from being redrawn
1714 RowManager.prototype.blockRedraw = function (){
1715  this.redrawBlock = true;
1716  this.redrawBlockRestoreConfig = false;
1717 };
1718 
1719 //restore table redrawing
1720 RowManager.prototype.restoreRedraw = function (){
1721  this.redrawBlock = false;
1722 
1723 
1724  if(this.redrawBlockRestoreConfig){
1725  this.refreshActiveData(this.redrawBlockRestoreConfig.stage, this.redrawBlockRestoreConfig.skipStage, this.redrawBlockRestoreConfig.renderInPosition)
1726 
1727  this.redrawBlockRestoreConfig = false;
1728  }else{
1729  if(this.redrawBlockRederInPosition){
1730  this.reRenderInPosition();
1731  }
1732  }
1733 
1734  this.redrawBlockRederInPosition = false;
1735 
1736 };
1737 
1738 //redraw table
1739 RowManager.prototype.redraw = function (force){
1740  var pos = 0,
1741  left = this.scrollLeft;
1742 
1743  this.adjustTableSize();
1744 
1745  this.table.tableWidth = this.table.element.clientWidth;
1746 
1747  if(!force){
1748  if(this.renderMode == "classic"){
1749 
1750  if(this.table.options.groupBy){
1751  this.refreshActiveData("group", false, false);
1752  }else{
1753  this._simpleRender();
1754  }
1755 
1756  }else{
1757  this.reRenderInPosition();
1758  this.scrollHorizontal(left);
1759  }
1760 
1761  if(!this.displayRowsCount){
1762  if(this.table.options.placeholder){
1763  this.getElement().appendChild(this.table.options.placeholder);
1764  }
1765  }
1766 
1767  }else{
1768  this.renderTable();
1769  }
1770 };
1771 
1772 RowManager.prototype.resetScroll = function(){
1773  this.element.scrollLeft = 0;
1774  this.element.scrollTop = 0;
1775 
1776  if(this.table.browser === "ie"){
1777  var event = document.createEvent("Event");
1778  event.initEvent("scroll", false, true);
1779  this.element.dispatchEvent(event);
1780  }else{
1781  this.element.dispatchEvent(new Event('scroll'));
1782  }
1783 };