4 var GroupComponent =
function (group){
6 this.type =
"GroupComponent";
9 GroupComponent.prototype.getKey =
function(){
10 return this._group.key;
13 GroupComponent.prototype.getField =
function(){
14 return this._group.field;
17 GroupComponent.prototype.getElement =
function(){
18 return this._group.element;
21 GroupComponent.prototype.getRows =
function(){
22 return this._group.getRows(
true);
25 GroupComponent.prototype.getSubGroups =
function(){
26 return this._group.getSubGroups(
true);
29 GroupComponent.prototype.getParentGroup =
function(){
30 return this._group.parent ? this._group.parent.getComponent() :
false;
33 GroupComponent.prototype.getVisibility =
function(){
34 return this._group.visible;
37 GroupComponent.prototype.show =
function(){
41 GroupComponent.prototype.hide =
function(){
45 GroupComponent.prototype.toggle =
function(){
46 this._group.toggleVisibility();
49 GroupComponent.prototype._getSelf =
function(){
53 GroupComponent.prototype.getTable =
function(){
54 return this._group.groupManager.table;
61 var Group =
function(groupManager, parent, level, key, field, generator, oldGroup){
63 this.groupManager = groupManager;
68 this.hasSubGroups = level < (groupManager.groupIDLookups.length - 1);
69 this.addRow = this.hasSubGroups ? this._addRowToGroup : this._addRow;
75 this.generator = generator;
76 this.elementContents =
false;
79 this.initialized =
false;
81 this.initialized =
false;
83 this.arrowElement =
false;
85 this.visible = oldGroup ? oldGroup.visible : (typeof groupManager.startOpen[level] !==
"undefined" ? groupManager.startOpen[level] : groupManager.startOpen[0]);
87 this.createElements();
90 this.createValueGroups();
93 Group.prototype.wipe =
function(){
94 if(this.groupList.length){
95 this.groupList.forEach(
function(group){
100 this.arrowElement =
false;
101 this.elementContents =
false;
105 Group.prototype.createElements =
function(){
106 var arrow = document.createElement(
"div");
107 arrow.classList.add(
"tabulator-arrow");
109 this.element = document.createElement(
"div");
110 this.element.classList.add(
"tabulator-row");
111 this.element.classList.add(
"tabulator-group");
112 this.element.classList.add(
"tabulator-group-level-" + this.level);
113 this.element.setAttribute(
"role",
"rowgroup");
115 this.arrowElement = document.createElement(
"div");
116 this.arrowElement.classList.add(
"tabulator-group-toggle");
117 this.arrowElement.appendChild(arrow);
120 if(this.groupManager.table.options.movableRows !==
false &&
this.groupManager.table.modExists(
"moveRow")){
121 this.groupManager.table.modules.moveRow.initializeGroupHeader(
this);
125 Group.prototype.createValueGroups =
function(){
126 var level = this.level + 1;
127 if(this.groupManager.allowedValues &&
this.groupManager.allowedValues[level]){
128 this.groupManager.allowedValues[level].forEach((value) => {
129 this._createGroup(value, level);
134 Group.prototype.addBindings =
function(){
136 dblTap, tapHold, tap, toggleElement;
140 if (
self.groupManager.table.options.groupClick){
141 self.element.addEventListener(
"click",
function(e){
142 self.groupManager.table.options.groupClick.call(
self.groupManager.table, e,
self.getComponent());
146 if (
self.groupManager.table.options.groupDblClick){
147 self.element.addEventListener(
"dblclick",
function(e){
148 self.groupManager.table.options.groupDblClick.call(
self.groupManager.table, e,
self.getComponent());
152 if (
self.groupManager.table.options.groupContext){
153 self.element.addEventListener(
"contextmenu",
function(e){
154 self.groupManager.table.options.groupContext.call(
self.groupManager.table, e,
self.getComponent());
158 if (
self.groupManager.table.options.groupTap){
162 self.element.addEventListener(
"touchstart",
function(e){
166 self.element.addEventListener(
"touchend",
function(e){
168 self.groupManager.table.options.groupTap(e,
self.getComponent());
175 if (
self.groupManager.table.options.groupDblTap){
179 self.element.addEventListener(
"touchend",
function(e){
182 clearTimeout(dblTap);
185 self.groupManager.table.options.groupDblTap(e,
self.getComponent());
188 dblTap = setTimeout(
function(){
189 clearTimeout(dblTap);
198 if (
self.groupManager.table.options.groupTapHold){
202 self.element.addEventListener(
"touchstart",
function(e){
203 clearTimeout(tapHold);
205 tapHold = setTimeout(
function(){
206 clearTimeout(tapHold);
209 self.groupManager.table.options.groupTapHold(e,
self.getComponent());
214 self.element.addEventListener(
"touchend",
function(e){
215 clearTimeout(tapHold);
222 if(
self.groupManager.table.options.groupToggleElement){
223 toggleElement =
self.groupManager.table.options.groupToggleElement ==
"arrow" ?
self.arrowElement :
self.element;
225 toggleElement.addEventListener(
"click",
function(e){
227 e.stopImmediatePropagation();
228 self.toggleVisibility();
235 Group.prototype._createGroup =
function(groupID, level){
236 var groupKey = level +
"_" + groupID;
237 var group =
new Group(this.groupManager,
this, level, groupID, this.groupManager.groupIDLookups[level].field,
this.groupManager.headerGenerator[level] ||
this.groupManager.headerGenerator[0],
this.old ?
this.old.groups[groupKey] :
false);
239 this.groups[groupKey] = group;
240 this.groupList.push(group);
243 Group.prototype._addRowToGroup =
function(row){
245 var level = this.level + 1;
247 if(this.hasSubGroups){
248 var groupID = this.groupManager.groupIDLookups[level].func(row.getData()),
249 groupKey = level +
"_" + groupID;
251 if(this.groupManager.allowedValues &&
this.groupManager.allowedValues[level]){
252 if(this.groups[groupKey]){
253 this.groups[groupKey].addRow(row);
256 if(!this.groups[groupKey]){
257 this._createGroup(groupID, level);
260 this.groups[groupKey].addRow(row);
265 Group.prototype._addRow =
function(row){
267 row.modules.group =
this;
270 Group.prototype.insertRow =
function(row, to, after){
271 var data = this.conformRowData({});
273 row.updateData(data);
275 var toIndex = this.rows.indexOf(to);
279 this.rows.splice(toIndex+1, 0, row);
281 this.rows.splice(toIndex, 0, row);
287 this.rows.unshift(row);
291 row.modules.group =
this;
293 this.generateGroupHeaderContents();
295 if(this.groupManager.table.modExists(
"columnCalcs") && this.groupManager.table.options.columnCalcs !=
"table"){
296 this.groupManager.table.modules.columnCalcs.recalcGroup(
this);
299 this.groupManager.updateGroupRows(
true);
302 Group.prototype.scrollHeader =
function(left){
303 this.arrowElement.style.marginLeft = left;
305 this.groupList.forEach(
function(child){
306 child.scrollHeader(left);
310 Group.prototype.getRowIndex =
function(row){
315 Group.prototype.conformRowData =
function(data){
317 data[this.field] = this.key;
319 console.warn(
"Data Conforming Error - Cannot conform row data to match new group as groupBy is a function");
323 data = this.parent.conformRowData(data);
331 Group.prototype.removeRow =
function(row){
332 var index = this.rows.indexOf(row);
333 var el = row.getElement();
337 this.rows.splice(index, 1);
340 if(!this.groupManager.table.options.groupValues && !
this.rows.length){
342 this.parent.removeGroup(
this);
344 this.groupManager.removeGroup(
this);
347 this.groupManager.updateGroupRows(
true);
351 el.parentNode.removeChild(el);
354 this.generateGroupHeaderContents();
356 if(this.groupManager.table.modExists(
"columnCalcs") && this.groupManager.table.options.columnCalcs !=
"table"){
357 this.groupManager.table.modules.columnCalcs.recalcGroup(
this);
363 Group.prototype.removeGroup =
function(group){
364 var groupKey = group.level +
"_" + group.key,
367 if(this.groups[groupKey]){
368 delete this.groups[groupKey];
370 index = this.groupList.indexOf(group);
373 this.groupList.splice(index, 1);
376 if(!this.groupList.length){
378 this.parent.removeGroup(
this);
380 this.groupManager.removeGroup(
this);
386 Group.prototype.getHeadersAndRows =
function(noCalc){
394 if(this.groupList.length){
395 this.groupList.forEach(
function(group){
396 output = output.concat(group.getHeadersAndRows(noCalc));
400 if(!noCalc && this.groupManager.table.options.columnCalcs !=
"table" &&
this.groupManager.table.modExists(
"columnCalcs") && this.groupManager.table.modules.columnCalcs.hasTopCalcs()){
402 this.calcs.top.detachElement();
403 this.calcs.top.deleteCells();
406 this.calcs.top = this.groupManager.table.modules.columnCalcs.generateTopRow(this.rows);
407 output.push(this.calcs.top);
410 output = output.concat(this.rows);
412 if(!noCalc && this.groupManager.table.options.columnCalcs !=
"table" &&
this.groupManager.table.modExists(
"columnCalcs") && this.groupManager.table.modules.columnCalcs.hasBottomCalcs()){
413 if(this.calcs.bottom){
414 this.calcs.bottom.detachElement();
415 this.calcs.bottom.deleteCells();
418 this.calcs.bottom = this.groupManager.table.modules.columnCalcs.generateBottomRow(this.rows);
419 output.push(this.calcs.bottom);
423 if(!this.groupList.length &&
this.groupManager.table.options.columnCalcs !=
"table"){
425 if(this.groupManager.table.modExists(
"columnCalcs")){
427 if(!noCalc && this.groupManager.table.modules.columnCalcs.hasTopCalcs()){
429 this.calcs.top.detachElement();
430 this.calcs.top.deleteCells();
433 if(this.groupManager.table.options.groupClosedShowCalcs){
434 this.calcs.top = this.groupManager.table.modules.columnCalcs.generateTopRow(this.rows);
435 output.push(this.calcs.top);
439 if(!noCalc && this.groupManager.table.modules.columnCalcs.hasBottomCalcs()){
440 if(this.calcs.bottom){
441 this.calcs.bottom.detachElement();
442 this.calcs.bottom.deleteCells();
445 if(this.groupManager.table.options.groupClosedShowCalcs){
446 this.calcs.bottom = this.groupManager.table.modules.columnCalcs.generateBottomRow(this.rows);
447 output.push(this.calcs.bottom);
458 Group.prototype.getData =
function(visible, transform){
464 if(!visible || (visible && this.visible)){
465 this.rows.forEach(
function(row){
466 output.push(row.getData(transform ||
"data"));
479 Group.prototype.getRowCount =
function(){
482 if(this.groupList.length){
483 this.groupList.forEach(
function(group){
484 count += group.getRowCount();
487 count = this.rows.length;
492 Group.prototype.toggleVisibility =
function(){
500 Group.prototype.hide =
function(){
501 this.visible =
false;
503 if(this.groupManager.table.rowManager.getRenderMode() ==
"classic" && !this.groupManager.table.options.pagination){
505 this.element.classList.remove(
"tabulator-group-visible");
507 if(this.groupList.length){
508 this.groupList.forEach(
function(group){
510 var rows = group.getHeadersAndRows();
512 rows.forEach(
function(row){
518 this.rows.forEach(
function(row){
519 var rowEl = row.getElement();
520 rowEl.parentNode.removeChild(rowEl);
524 this.groupManager.table.rowManager.setDisplayRows(this.groupManager.updateGroupRows(), this.groupManager.getDisplayIndex());
526 this.groupManager.table.rowManager.checkClassicModeGroupHeaderWidth();
529 this.groupManager.updateGroupRows(
true);
532 this.groupManager.table.options.groupVisibilityChanged.call(this.table, this.getComponent(),
false);
535 Group.prototype.show =
function(){
540 if(this.groupManager.table.rowManager.getRenderMode() ==
"classic" && !this.groupManager.table.options.pagination){
542 this.element.classList.add(
"tabulator-group-visible");
544 var prev =
self.getElement();
546 if(this.groupList.length){
547 this.groupList.forEach(
function(group){
548 var rows = group.getHeadersAndRows();
550 rows.forEach(
function(row){
551 var rowEl = row.getElement();
552 prev.parentNode.insertBefore(rowEl, prev.nextSibling);
559 self.rows.forEach(
function(row){
560 var rowEl = row.getElement();
561 prev.parentNode.insertBefore(rowEl, prev.nextSibling);
567 this.groupManager.table.rowManager.setDisplayRows(this.groupManager.updateGroupRows(), this.groupManager.getDisplayIndex());
569 this.groupManager.table.rowManager.checkClassicModeGroupHeaderWidth();
571 this.groupManager.updateGroupRows(
true);
574 this.groupManager.table.options.groupVisibilityChanged.call(this.table, this.getComponent(),
true);
577 Group.prototype._visSet =
function(){
580 if(typeof this.visible ==
"function"){
582 this.rows.forEach(
function(row){
583 data.push(row.getData());
586 this.visible = this.visible(this.key, this.getRowCount(), data, this.getComponent());
590 Group.prototype.getRowGroup =
function(row){
592 if(this.groupList.length){
593 this.groupList.forEach(
function(group){
594 var result = group.getRowGroup(row);
601 if(this.rows.find(
function(item){
611 Group.prototype.getSubGroups =
function(component){
614 this.groupList.forEach(
function(child){
615 output.push(component ? child.getComponent() : child);
621 Group.prototype.getRows =
function(compoment){
624 this.rows.forEach(
function(row){
625 output.push(compoment ? row.getComponent() : row);
631 Group.prototype.generateGroupHeaderContents =
function(){
634 this.rows.forEach(
function(row){
635 data.push(row.getData());
638 this.elementContents = this.generator(this.key, this.getRowCount(), data, this.getComponent());
640 while(this.element.firstChild) this.element.removeChild(this.element.firstChild);
642 if(typeof this.elementContents ===
"string"){
643 this.element.innerHTML = this.elementContents;
645 this.element.appendChild(this.elementContents);
648 this.element.insertBefore(this.arrowElement, this.element.firstChild);
653 Group.prototype.getElement =
function(){
654 this.addBindingsd =
false;
659 this.element.classList.add(
"tabulator-group-visible");
661 this.element.classList.remove(
"tabulator-group-visible");
664 for(var i = 0; i < this.element.childNodes.length; ++i){
665 this.element.childNodes[i].parentNode.removeChild(this.element.childNodes[i]);
668 this.generateGroupHeaderContents();
675 Group.prototype.detachElement =
function(){
676 if (this.element && this.element.parentNode){
677 this.element.parentNode.removeChild(this.element);
682 Group.prototype.normalizeHeight =
function(){
683 this.setHeight(this.element.clientHeight);
686 Group.prototype.initialize =
function(force){
687 if(!this.initialized || force){
688 this.normalizeHeight();
689 this.initialized =
true;
693 Group.prototype.reinitialize =
function(){
694 this.initialized =
false;
697 if(Tabulator.prototype.helpers.elVisible(
this.element)){
698 this.initialize(
true);
702 Group.prototype.setHeight =
function(height){
703 if(this.height != height){
704 this.height = height;
705 this.outerHeight = this.element.offsetHeight;
710 Group.prototype.getHeight =
function(){
711 return this.outerHeight;
714 Group.prototype.getGroup =
function(){
718 Group.prototype.reinitializeHeight =
function(){
720 Group.prototype.calcHeight =
function(){
722 Group.prototype.setCellHeight =
function(){
724 Group.prototype.clearCellHeight =
function(){
729 Group.prototype.getComponent =
function(){
730 return new GroupComponent(
this);
737 var GroupRows =
function(table){
741 this.groupIDLookups =
false;
742 this.startOpen = [
function(){
return false;}];
743 this.headerGenerator = [
function(){
return "";}];
745 this.allowedValues =
false;
747 this.displayIndex = 0;
751 GroupRows.prototype.initialize =
function(){
753 groupBy =
self.table.options.groupBy,
754 startOpen =
self.table.options.groupStartOpen,
755 groupHeader =
self.table.options.groupHeader;
757 this.allowedValues =
self.table.options.groupValues;
759 if(Array.isArray(groupBy) && Array.isArray(groupHeader) && groupBy.length > groupHeader.length){
760 console.warn(
"Error creating group headers, groupHeader array is shorter than groupBy array");
763 self.headerGenerator = [
function(){
return "";}];
764 this.startOpen = [
function(){
return false;}];
766 self.table.modules.localize.bind(
"groups|item",
function(langValue, lang){
767 self.headerGenerator[0] =
function(value, count, data){
768 return (typeof value ===
"undefined" ?
"" : value) +
"<span>(" + count +
" " + ((count === 1) ? langValue : lang.groups.items) +
")</span>";
772 this.groupIDLookups = [];
774 if(Array.isArray(groupBy) || groupBy){
775 if(this.table.modExists(
"columnCalcs") && this.table.options.columnCalcs !=
"table" && this.table.options.columnCalcs !=
"both"){
776 this.table.modules.columnCalcs.removeCalcs();
779 if(this.table.modExists(
"columnCalcs") && this.table.options.columnCalcs !=
"group"){
781 var cols = this.table.columnManager.getRealColumns();
783 cols.forEach(
function(col){
784 if(col.definition.topCalc){
785 self.table.modules.columnCalcs.initializeTopRow();
788 if(col.definition.bottomCalc){
789 self.table.modules.columnCalcs.initializeBottomRow();
797 if(!Array.isArray(groupBy)){
801 groupBy.forEach(
function(group, i){
802 var lookupFunc, column;
804 if(typeof group ==
"function"){
807 column =
self.table.columnManager.getColumnByField(group);
810 lookupFunc =
function(data){
811 return column.getFieldValue(data);
814 lookupFunc =
function(data){
820 self.groupIDLookups.push({
821 field: typeof group ===
"function" ?
false : group,
823 values:
self.allowedValues ?
self.allowedValues[i] :
false,
831 if(!Array.isArray(startOpen)){
832 startOpen = [startOpen];
835 startOpen.forEach(
function(level){
836 level = typeof level ==
"function" ? level :
function(){
return true;};
839 self.startOpen = startOpen;
843 self.headerGenerator = Array.isArray(groupHeader) ? groupHeader : [groupHeader];
846 this.initialized =
true;
850 GroupRows.prototype.setDisplayIndex =
function(index){
851 this.displayIndex = index;
854 GroupRows.prototype.getDisplayIndex =
function(){
855 return this.displayIndex;
860 GroupRows.prototype.getRows =
function(rows){
861 if(this.groupIDLookups.length){
863 this.table.options.dataGrouping.call(this.table);
865 this.generateGroups(rows);
867 if(this.table.options.dataGrouped){
868 this.table.options.dataGrouped.call(this.table, this.getGroups(
true));
871 return this.updateGroupRows();
874 return rows.slice(0);
879 GroupRows.prototype.getGroups =
function(compoment){
880 var groupComponents = [];
882 this.groupList.forEach(
function(group){
883 groupComponents.push(compoment ? group.getComponent() : group);
886 return groupComponents;
889 GroupRows.prototype.wipe =
function(){
890 this.groupList.forEach(
function(group){
895 GroupRows.prototype.pullGroupListData =
function(groupList) {
897 var groupListData = [];
899 groupList.forEach(
function(group) {
900 var groupHeader = {};
901 groupHeader.level = 0;
902 groupHeader.rowCount = 0;
903 groupHeader.headerContent =
"";
906 if (group.hasSubGroups) {
907 childData =
self.pullGroupListData(group.groupList);
909 groupHeader.level = group.level;
910 groupHeader.rowCount = childData.length - group.groupList.length;
911 groupHeader.headerContent = group.generator(group.key, groupHeader.rowCount, group.rows, group);
913 groupListData.push(groupHeader);
914 groupListData = groupListData.concat(childData);
918 groupHeader.level = group.level;
919 groupHeader.headerContent = group.generator(group.key, group.rows.length, group.rows, group);
920 groupHeader.rowCount = group.getRows().length;
922 groupListData.push(groupHeader);
924 group.getRows().forEach(
function(row) {
925 groupListData.push(row.getData(
"data"));
933 GroupRows.prototype.getGroupedData =
function(){
935 return this.pullGroupListData(this.groupList);
938 GroupRows.prototype.getRowGroup =
function(row){
941 this.groupList.forEach(
function(group){
942 var result = group.getRowGroup(row);
952 GroupRows.prototype.countGroups =
function(){
953 return this.groupList.length;
956 GroupRows.prototype.generateGroups =
function(rows){
958 oldGroups =
self.groups;
963 if(this.allowedValues && this.allowedValues[0]){
964 this.allowedValues[0].forEach(
function(value){
965 self.createGroup(value, 0, oldGroups);
968 rows.forEach(
function(row){
969 self.assignRowToExistingGroup(row, oldGroups);
972 rows.forEach(
function(row){
973 self.assignRowToGroup(row, oldGroups);
979 GroupRows.prototype.createGroup =
function(groupID, level, oldGroups){
980 var groupKey = level +
"_" + groupID,
983 oldGroups = oldGroups || [];
985 group =
new Group(
this,
false, level, groupID, this.groupIDLookups[0].field, this.headerGenerator[0], oldGroups[groupKey]);
987 this.groups[groupKey] = group;
988 this.groupList.push(group);
1002 GroupRows.prototype.assignRowToExistingGroup =
function(row, oldGroups){
1003 var groupID = this.groupIDLookups[0].func(row.getData()),
1004 groupKey =
"0_" + groupID;
1006 if(this.groups[groupKey]){
1007 this.groups[groupKey].addRow(row);
1012 GroupRows.prototype.assignRowToGroup =
function(row, oldGroups){
1013 var groupID = this.groupIDLookups[0].func(row.getData()),
1014 newGroupNeeded = !this.groups[
"0_" + groupID];
1017 this.createGroup(groupID, 0, oldGroups);
1020 this.groups[
"0_" + groupID].addRow(row);
1022 return !newGroupNeeded;
1027 GroupRows.prototype.updateGroupRows =
function(force){
1032 self.groupList.forEach(
function(group){
1033 output = output.concat(group.getHeadersAndRows());
1039 var displayIndex =
self.table.rowManager.setDisplayRows(output, this.getDisplayIndex());
1041 if(displayIndex !==
true){
1042 this.setDisplayIndex(displayIndex);
1045 self.table.rowManager.refreshActiveData(
"group",
true,
true);
1051 GroupRows.prototype.scrollHeaders =
function(left){
1054 this.groupList.forEach(
function(group){
1055 group.scrollHeader(left);
1059 GroupRows.prototype.removeGroup =
function(group){
1060 var groupKey = group.level +
"_" + group.key,
1063 if(this.groups[groupKey]){
1064 delete this.groups[groupKey];
1066 index = this.groupList.indexOf(group);
1069 this.groupList.splice(index, 1);
1074 Tabulator.prototype.registerModule(
"groupRows", GroupRows);