jquery.tabulator
Version:
Interactive table generation plugin for jQuery UI
2,342 lines (1,326 loc) • 423 kB
JavaScript
'use strict';
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
/* Tabulator v3.5.3 (c) Oliver Folkerd */
/*
* This file is part of the Tabulator package.
*
* (c) Oliver Folkerd <oliver.folkerd@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* Full Documentation & Demos can be found at: http://olifolkerd.github.io/tabulator/
*
*/
(function (factory) {
"use strict";
if (typeof define === 'function' && define.amd) {
define(['jquery'], factory);
} else if (typeof module !== 'undefined' && module.exports) {
module.exports = factory(require('jquery'));
} else {
factory(jQuery);
}
})(function ($, undefined) {
(function () {
'use strict';
// https://tc39.github.io/ecma262/#sec-array.prototype.findIndex
if (!Array.prototype.findIndex) {
Object.defineProperty(Array.prototype, 'findIndex', {
value: function value(predicate) {
// 1. Let O be ? ToObject(this value).
if (this == null) {
throw new TypeError('"this" is null or not defined');
}
var o = Object(this);
// 2. Let len be ? ToLength(? Get(O, "length")).
var len = o.length >>> 0;
// 3. If IsCallable(predicate) is false, throw a TypeError exception.
if (typeof predicate !== 'function') {
throw new TypeError('predicate must be a function');
}
// 4. If thisArg was supplied, let T be thisArg; else let T be undefined.
var thisArg = arguments[1];
// 5. Let k be 0.
var k = 0;
// 6. Repeat, while k < len
while (k < len) {
// a. Let Pk be ! ToString(k).
// b. Let kValue be ? Get(O, Pk).
// c. Let testResult be ToBoolean(? Call(predicate, T, « kValue, k, O »)).
// d. If testResult is true, return k.
var kValue = o[k];
if (predicate.call(thisArg, kValue, k, o)) {
return k;
}
// e. Increase k by 1.
k++;
}
// 7. Return -1.
return -1;
}
});
}
// https://tc39.github.io/ecma262/#sec-array.prototype.find
if (!Array.prototype.find) {
Object.defineProperty(Array.prototype, 'find', {
value: function value(predicate) {
// 1. Let O be ? ToObject(this value).
if (this == null) {
throw new TypeError('"this" is null or not defined');
}
var o = Object(this);
// 2. Let len be ? ToLength(? Get(O, "length")).
var len = o.length >>> 0;
// 3. If IsCallable(predicate) is false, throw a TypeError exception.
if (typeof predicate !== 'function') {
throw new TypeError('predicate must be a function');
}
// 4. If thisArg was supplied, let T be thisArg; else let T be undefined.
var thisArg = arguments[1];
// 5. Let k be 0.
var k = 0;
// 6. Repeat, while k < len
while (k < len) {
// a. Let Pk be ! ToString(k).
// b. Let kValue be ? Get(O, Pk).
// c. Let testResult be ToBoolean(? Call(predicate, T, « kValue, k, O »)).
// d. If testResult is true, return kValue.
var kValue = o[k];
if (predicate.call(thisArg, kValue, k, o)) {
return kValue;
}
// e. Increase k by 1.
k++;
}
// 7. Return undefined.
return undefined;
}
});
}
var ColumnManager = function ColumnManager(table) {
this.table = table; //hold parent table
this.headersElement = $("<div class='tabulator-headers'></div>");
this.element = $("<div class='tabulator-header'></div>"); //containing element
this.rowManager = null; //hold row manager object
this.columns = []; // column definition object
this.columnsByIndex = []; //columns by index
this.columnsByField = []; //columns by field
this.scrollLeft = 0;
this.element.prepend(this.headersElement);
};
////////////// Setup Functions /////////////////
//link to row manager
ColumnManager.prototype.setRowManager = function (manager) {
this.rowManager = manager;
};
//return containing element
ColumnManager.prototype.getElement = function () {
return this.element;
};
//return header containing element
ColumnManager.prototype.getHeadersElement = function () {
return this.headersElement;
};
//scroll horizontally to match table body
ColumnManager.prototype.scrollHorizontal = function (left) {
var hozAdjust = 0,
scrollWidth = this.element[0].scrollWidth - this.table.element.innerWidth();
this.element.scrollLeft(left);
//adjust for vertical scrollbar moving table when present
if (left > scrollWidth) {
hozAdjust = left - scrollWidth;
this.element.css("margin-left", -hozAdjust);
} else {
this.element.css("margin-left", 0);
}
//keep frozen columns fixed in position
//this._calcFrozenColumnsPos(hozAdjust + 3);
this.scrollLeft = left;
if (this.table.extExists("frozenColumns")) {
this.table.extensions.frozenColumns.layout();
}
};
///////////// Column Setup Functions /////////////
ColumnManager.prototype.setColumns = function (cols, row) {
var self = this;
self.headersElement.empty();
self.columns = [];
self.columnsByIndex = [];
self.columnsByField = [];
//reset frozen columns
if (self.table.extExists("frozenColumns")) {
self.table.extensions.frozenColumns.reset();
}
cols.forEach(function (def, i) {
self._addColumn(def);
});
self._reIndexColumns();
if (self.table.options.responsiveLayout && self.table.extExists("responsiveLayout", true)) {
self.table.extensions.responsiveLayout.initialize();
}
self.redraw(true);
};
ColumnManager.prototype._addColumn = function (definition, before, nextToColumn) {
var column = new Column(definition, this);
var index = nextToColumn ? this.findColumnIndex(nextToColumn) : nextToColumn;
if (nextToColumn && index > -1) {
var parentIndex = this.columns.indexOf(nextToColumn.getTopColumn());
if (before) {
this.columns.splice(parentIndex, 0, column);
nextToColumn.getElement().before(column.getElement());
} else {
this.columns.splice(parentIndex + 1, 0, column);
nextToColumn.getElement().after(column.getElement());
}
} else {
if (before) {
this.columns.unshift(column);
this.headersElement.prepend(column.getElement());
} else {
this.columns.push(column);
this.headersElement.append(column.getElement());
}
}
return column;
};
ColumnManager.prototype.registerColumnField = function (col) {
if (col.definition.field) {
this.columnsByField[col.definition.field] = col;
}
};
ColumnManager.prototype.registerColumnPosition = function (col) {
this.columnsByIndex.push(col);
};
ColumnManager.prototype._reIndexColumns = function () {
this.columnsByIndex = [];
this.columns.forEach(function (column) {
column.reRegisterPosition();
});
};
//ensure column headers take up the correct amount of space in column groups
ColumnManager.prototype._verticalAlignHeaders = function () {
var self = this;
self.columns.forEach(function (column) {
column.clearVerticalAlign();
});
self.columns.forEach(function (column) {
column.verticalAlign(self.table.options.columnVertAlign);
});
self.rowManager.adjustTableSize();
};
//////////////// Column Details /////////////////
ColumnManager.prototype.findColumn = function (subject) {
var self = this;
if ((typeof subject === 'undefined' ? 'undefined' : _typeof(subject)) == "object") {
if (subject instanceof Column) {
//subject is column element
return subject;
} else if (subject instanceof ColumnComponent) {
//subject is public column component
return subject._getSelf() || false;
} else if (subject instanceof jQuery) {
//subject is a jquery element of the column header
var match = self.columns.find(function (column) {
return column.element === subject;
});
return match || false;
}
} else {
//subject should be treated as the field name of the column
return this.columnsByField[subject] || false;
}
//catch all for any other type of input
return false;
};
ColumnManager.prototype.getColumnByField = function (field) {
return this.columnsByField[field];
};
ColumnManager.prototype.getColumnByIndex = function (index) {
return this.columnsByIndex[index];
};
ColumnManager.prototype.getColumns = function () {
return this.columns;
};
ColumnManager.prototype.findColumnIndex = function (column) {
return this.columnsByIndex.findIndex(function (col) {
return column === col;
});
};
//return all columns that are not groups
ColumnManager.prototype.getRealColumns = function () {
return this.columnsByIndex;
};
//travers across columns and call action
ColumnManager.prototype.traverse = function (callback) {
var self = this;
self.columnsByIndex.forEach(function (column, i) {
callback(column, i);
});
};
//get defintions of actual columns
ColumnManager.prototype.getDefinitions = function (active) {
var self = this,
output = [];
self.columnsByIndex.forEach(function (column) {
if (!active || active && column.visible) {
output.push(column.getDefinition());
}
});
return output;
};
//get full nested definition tree
ColumnManager.prototype.getDefinitionTree = function () {
var self = this,
output = [];
self.columns.forEach(function (column) {
output.push(column.getDefinition(true));
});
return output;
};
ColumnManager.prototype.getComponents = function (structured) {
var self = this,
output = [],
columns = structured ? self.columns : self.columnsByIndex;
columns.forEach(function (column) {
output.push(column.getComponent());
});
return output;
};
ColumnManager.prototype.getWidth = function () {
var width = 0;
this.columnsByIndex.forEach(function (column) {
if (column.visible) {
width += column.getWidth();
}
});
return width;
};
ColumnManager.prototype.moveColumn = function (from, to, after) {
this._moveColumnInArray(this.columns, from, to, after);
this._moveColumnInArray(this.columnsByIndex, from, to, after, true);
if (this.table.options.responsiveLayout && this.table.extExists("responsiveLayout", true)) {
this.table.extensions.responsiveLayout.initialize();
}
if (this.table.options.columnMoved) {
this.table.options.columnMoved(from.getComponent(), this.table.columnManager.getComponents());
}
if (this.table.options.persistentLayout && this.table.extExists("persistence", true)) {
this.table.extensions.persistence.save("columns");
}
};
ColumnManager.prototype._moveColumnInArray = function (columns, from, to, after, updateRows) {
var fromIndex = columns.indexOf(from),
toIndex;
if (fromIndex > -1) {
columns.splice(fromIndex, 1);
toIndex = columns.indexOf(to);
if (toIndex > -1) {
if (after) {
toIndex = toIndex + 1;
}
} else {
toIndex = fromIndex;
}
columns.splice(toIndex, 0, from);
if (updateRows) {
this.table.rowManager.rows.forEach(function (row) {
if (row.cells.length) {
var cell = row.cells.splice(fromIndex, 1)[0];
row.cells.splice(toIndex, 0, cell);
}
});
}
}
};
ColumnManager.prototype.scrollToColumn = function (column, position, ifVisible) {
var left = 0,
offset = 0,
adjust = 0;
if (typeof position === "undefined") {
position = this.table.options.scrollToColumnPosition;
}
if (typeof ifVisible === "undefined") {
ifVisible = this.table.options.scrollToColumnIfVisible;
}
if (column.visible) {
//align to correct position
switch (position) {
case "middle":
case "center":
adjust = -this.element[0].clientWidth / 2;
break;
case "right":
adjust = column.element.innerWidth() - this.headersElement.innerWidth();
break;
}
//check column visibility
if (!ifVisible) {
offset = column.element.position().left;
if (offset > 0 && offset + column.element.outerWidth() < this.element[0].clientWidth) {
return false;
}
}
//calculate scroll position
left = column.element.position().left + this.element.scrollLeft() + adjust;
left = Math.max(Math.min(left, this.table.rowManager.element[0].scrollWidth - this.table.rowManager.element[0].clientWidth), 0);
this.table.rowManager.scrollHorizontal(left);
this.scrollHorizontal(left);
return true;
} else {
console.warn("Scroll Error - Column not visible");
return false;
}
};
//////////////// Cell Management /////////////////
ColumnManager.prototype.generateCells = function (row) {
var self = this;
var cells = [];
self.columnsByIndex.forEach(function (column) {
cells.push(column.generateCell(row));
});
return cells;
};
//////////////// Column Management /////////////////
ColumnManager.prototype.getFlexBaseWidth = function () {
var self = this,
totalWidth = self.table.element.innerWidth(),
//table element width
fixedWidth = 0;
//adjust for vertical scrollbar if present
if (self.rowManager.element[0].scrollHeight > self.rowManager.element.innerHeight()) {
totalWidth -= self.rowManager.element[0].offsetWidth - self.rowManager.element[0].clientWidth;
}
this.columnsByIndex.forEach(function (column) {
var width, minWidth, colWidth;
if (column.visible) {
width = column.definition.width || 0;
minWidth = typeof column.minWidth == "undefined" ? self.table.options.columnMinWidth : parseInt(column.minWidth);
if (typeof width == "string") {
if (width.indexOf("%") > -1) {
colWidth = totalWidth / 100 * parseInt(width);
} else {
colWidth = parseInt(width);
}
} else {
colWidth = width;
}
fixedWidth += colWidth > minWidth ? colWidth : minWidth;
}
});
return fixedWidth;
};
ColumnManager.prototype.addColumn = function (definition, before, nextToColumn) {
var column = this._addColumn(definition, before, nextToColumn);
this._reIndexColumns();
if (this.table.options.responsiveLayout && this.table.extExists("responsiveLayout", true)) {
this.table.extensions.responsiveLayout.initialize();
}
if (this.table.extExists("columnCalcs")) {
this.table.extensions.columnCalcs.recalc(this.table.rowManager.activeRows);
}
this.redraw();
if (this.table.extensions.layout.getMode() != "fitColumns") {
column.reinitializeWidth();
}
this._verticalAlignHeaders();
this.table.rowManager.reinitialize();
};
//remove column from system
ColumnManager.prototype.deregisterColumn = function (column) {
var field = column.getField(),
index;
//remove from field list
if (field) {
delete this.columnsByField[field];
}
//remove from index list
index = this.columnsByIndex.indexOf(column);
if (index > -1) {
this.columnsByIndex.splice(index, 1);
}
//remove from column list
index = this.columns.indexOf(column);
if (index > -1) {
this.columns.splice(index, 1);
}
if (this.table.options.responsiveLayout && this.table.extExists("responsiveLayout", true)) {
this.table.extensions.responsiveLayout.initialize();
}
this.redraw();
};
//redraw columns
ColumnManager.prototype.redraw = function (force) {
if (force) {
if (this.element.is(":visible")) {
this._verticalAlignHeaders();
}
this.table.rowManager.resetScroll();
this.table.rowManager.reinitialize();
}
if (this.table.extensions.layout.getMode() == "fitColumns") {
this.table.extensions.layout.layout();
} else {
if (force) {
this.table.extensions.layout.layout();
} else {
if (this.table.options.responsiveLayout && this.table.extExists("responsiveLayout", true)) {
this.table.extensions.responsiveLayout.update();
}
}
}
if (this.table.extExists("frozenColumns")) {
this.table.extensions.frozenColumns.layout();
}
if (this.table.extExists("columnCalcs")) {
this.table.extensions.columnCalcs.recalc(this.table.rowManager.activeRows);
}
if (force) {
if (this.table.options.persistentLayout && this.table.extExists("persistence", true)) {
this.table.extensions.persistence.save("columns");
}
if (this.table.extExists("columnCalcs")) {
this.table.extensions.columnCalcs.redraw();
}
}
this.table.footerManager.redraw();
};
//public column object
var ColumnComponent = function ColumnComponent(column) {
this.column = column;
this.type = "ColumnComponent";
};
ColumnComponent.prototype.getElement = function () {
return this.column.getElement();
};
ColumnComponent.prototype.getDefinition = function () {
return this.column.getDefinition();
};
ColumnComponent.prototype.getField = function () {
return this.column.getField();
};
ColumnComponent.prototype.getCells = function () {
var cells = [];
this.column.cells.forEach(function (cell) {
cells.push(cell.getComponent());
});
return cells;
};
ColumnComponent.prototype.getVisibility = function () {
return this.column.visible;
};
ColumnComponent.prototype.show = function () {
if (this.column.isGroup) {
this.column.columns.forEach(function (column) {
column.show();
});
} else {
this.column.show();
}
};
ColumnComponent.prototype.hide = function () {
if (this.column.isGroup) {
this.column.columns.forEach(function (column) {
column.hide();
});
} else {
this.column.hide();
}
};
ColumnComponent.prototype.toggle = function () {
if (this.column.visible) {
this.hide();
} else {
this.show();
}
};
ColumnComponent.prototype.delete = function () {
this.column.delete();
};
ColumnComponent.prototype.getSubColumns = function () {
var output = [];
if (this.column.columns.length) {
this.column.columns.forEach(function (column) {
output.push(column.getComponent());
});
}
return output;
};
ColumnComponent.prototype.getParentColumn = function () {
return this.column.parent instanceof Column ? this.column.parent.getComponent() : false;
};
ColumnComponent.prototype._getSelf = function () {
return this.column;
};
ColumnComponent.prototype.scrollTo = function () {
this.column.table.columManager.scrollToColumn(this.column);
};
var Column = function Column(def, parent) {
var self = this;
this.table = parent.table;
this.definition = def; //column definition
this.parent = parent; //hold parent object
this.type = "column"; //type of element
this.columns = []; //child columns
this.cells = []; //cells bound to this column
this.element = $("<div class='tabulator-col' role='columnheader' aria-sort='none'></div>"); //column header element
this.contentElement = false;
this.groupElement = $("<div class='tabulator-col-group-cols'></div>"); //column group holder element
this.isGroup = false;
this.tooltip = false; //hold column tooltip
this.hozAlign = ""; //horizontal text alignment
//multi dimentional filed handling
this.field = "";
this.fieldStructure = "";
this.getFieldValue = "";
this.setFieldValue = "";
this.setField(this.definition.field);
this.extensions = {}; //hold extension variables;
this.cellEvents = {
cellClick: false,
cellDblClick: false,
cellContext: false,
cellTap: false,
cellDblTap: false,
cellTapHold: false
};
this.width = null; //column width
this.minWidth = null; //column minimum width
this.widthFixed = false; //user has specified a width for this column
this.visible = true; //default visible state
//initialize column
if (def.columns) {
this.isGroup = true;
def.columns.forEach(function (def, i) {
var newCol = new Column(def, self);
self.attachColumn(newCol);
});
self.checkColumnVisibility();
} else {
parent.registerColumnField(this);
}
if (def.rowHandle && this.table.options.movableRows !== false && this.table.extExists("moveRow")) {
this.table.extensions.moveRow.setHandle(true);
}
this._mapDepricatedFunctionality();
this._buildHeader();
};
//////////////// Setup Functions /////////////////
Column.prototype._mapDepricatedFunctionality = function (field) {
if (this.definition.tooltipHeader) {
console.warn("The%c tooltipHeader%c column definition property has been depricated and will be removed in version 4.0, use %c headerTooltip%c instead.", "font-weight:bold;", "font-weight:regular;", "font-weight:bold;", "font-weight:regular;");
if (typeof this.definition.headerTooltip == "undefined") {
this.definition.headerTooltip = this.definition.tooltipHeader;
}
}
};
Column.prototype.setField = function (field) {
this.field = field;
this.fieldStructure = field ? field.split(".") : [];
this.getFieldValue = this.fieldStructure.length > 1 ? this._getNestedData : this._getFlatData;
this.setFieldValue = this.fieldStructure.length > 1 ? this._setNesteData : this._setFlatData;
};
//register column position with column manager
Column.prototype.registerColumnPosition = function (column) {
this.parent.registerColumnPosition(column);
};
//register column position with column manager
Column.prototype.registerColumnField = function (column) {
this.parent.registerColumnField(column);
};
//trigger position registration
Column.prototype.reRegisterPosition = function () {
if (this.isGroup) {
this.columns.forEach(function (column) {
column.reRegisterPosition();
});
} else {
this.registerColumnPosition(this);
}
};
Column.prototype.setTooltip = function () {
var self = this,
def = self.definition;
//set header tooltips
var tooltip = def.headerTooltip || def.tooltip === false ? def.headerTooltip : self.table.options.tooltipsHeader;
if (tooltip) {
if (tooltip === true) {
if (def.field) {
self.table.extensions.localize.bind("columns|" + def.field, function (value) {
self.element.attr("title", value || def.title);
});
} else {
self.element.attr("title", def.title);
}
} else {
if (typeof tooltip == "function") {
tooltip = tooltip(self.getComponent());
if (tooltip === false) {
tooltip = "";
}
}
self.element.attr("title", tooltip);
}
} else {
self.element.attr("title", "");
}
};
//build header element
Column.prototype._buildHeader = function () {
var self = this,
def = self.definition,
dblTap,
tapHold,
tap;
self.element.empty();
self.contentElement = self._buildColumnHeaderContent();
self.element.append(self.contentElement);
if (self.isGroup) {
self._buildGroupHeader();
} else {
self._buildColumnHeader();
}
self.setTooltip();
//set resizable handles
if (self.table.options.resizableColumns && self.table.extExists("resizeColumns")) {
self.table.extensions.resizeColumns.initializeColumn("header", self, self.element);
}
//set resizable handles
if (def.headerFilter && self.table.extExists("filter") && self.table.extExists("edit")) {
if (typeof def.headerFilterPlaceholder !== "undefined" && def.field) {
self.table.extensions.localize.setHeaderFilterColumnPlaceholder(def.field, def.headerFilterPlaceholder);
}
self.table.extensions.filter.initializeColumn(self);
}
//set resizable handles
if (self.table.extExists("frozenColumns")) {
self.table.extensions.frozenColumns.initializeColumn(self);
}
//set movable column
if (self.table.options.movableColumns && !self.isGroup && self.table.extExists("moveColumn")) {
self.table.extensions.moveColumn.initializeColumn(self);
}
//set calcs column
if ((def.topCalc || def.bottomCalc) && self.table.extExists("columnCalcs")) {
self.table.extensions.columnCalcs.initializeColumn(self);
}
//update header tooltip on mouse enter
self.element.on("mouseenter", function (e) {
self.setTooltip();
});
//setup header click event bindings
if (typeof def.headerClick == "function") {
self.element.on("click", function (e) {
def.headerClick(e, self.getComponent());
});
}
if (typeof def.headerDblClick == "function") {
self.element.on("dblclick", function (e) {
def.headerDblClick(e, self.getComponent());
});
}
if (typeof def.headerContext == "function") {
self.element.on("contextmenu", function (e) {
def.headerContext(e, self.getComponent());
});
}
//setup header tap event bindings
if (typeof def.headerTap == "function") {
tap = false;
self.element.on("touchstart", function (e) {
tap = true;
});
self.element.on("touchend", function (e) {
if (tap) {
def.headerTap(e, self.getComponent());
}
tap = false;
});
}
if (typeof def.headerDblTap == "function") {
dblTap = null;
self.element.on("touchend", function (e) {
if (dblTap) {
clearTimeout(dblTap);
dblTap = null;
def.headerDblTap(e, self.getComponent());
} else {
dblTap = setTimeout(function () {
clearTimeout(dblTap);
dblTap = null;
}, 300);
}
});
}
if (typeof def.headerTapHold == "function") {
tapHold = null;
self.element.on("touchstart", function (e) {
clearTimeout(tapHold);
tapHold = setTimeout(function () {
clearTimeout(tapHold);
tapHold = null;
tap = false;
def.headerTapHold(e, self.getComponent());
}, 1000);
});
self.element.on("touchend", function (e) {
clearTimeout(tapHold);
tapHold = null;
});
}
//store column cell click event bindings
if (typeof def.cellClick == "function") {
self.cellEvents.cellClick = def.cellClick;
}
if (typeof def.cellDblClick == "function") {
self.cellEvents.cellDblClick = def.cellDblClick;
}
if (typeof def.cellContext == "function") {
self.cellEvents.cellContext = def.cellContext;
}
//setup column cell tap event bindings
if (typeof def.cellTap == "function") {
self.cellEvents.cellTap = def.cellTap;
}
if (typeof def.cellDblTap == "function") {
self.cellEvents.cellDblTap = def.cellDblTap;
}
if (typeof def.cellTapHold == "function") {
self.cellEvents.cellTapHold = def.cellTapHold;
}
//setup column cell edit callbacks
if (typeof def.cellEdited == "function") {
self.cellEvents.cellEdited = def.cellEdited;
}
if (typeof def.cellEditing == "function") {
self.cellEvents.cellEditing = def.cellEditing;
}
if (typeof def.cellEditCancelled == "function") {
self.cellEvents.cellEditCancelled = def.cellEditCancelled;
}
};
//build header element for header
Column.prototype._buildColumnHeader = function () {
var self = this,
def = self.definition,
table = self.table,
sortable;
//set column sorter
if (table.extExists("sort")) {
table.extensions.sort.initializeColumn(self, self.contentElement);
}
//set column formatter
if (table.extExists("format")) {
table.extensions.format.initializeColumn(self);
}
//set column editor
if (typeof def.editor != "undefined" && table.extExists("edit")) {
table.extensions.edit.initializeColumn(self);
}
//set colum validator
if (typeof def.validator != "undefined" && table.extExists("validate")) {
table.extensions.validate.initializeColumn(self);
}
//set column mutator
if (table.extExists("mutator")) {
table.extensions.mutator.initializeColumn(self);
}
//set column accessor
if (table.extExists("accessor")) {
table.extensions.accessor.initializeColumn(self);
}
//set respoviveLayout
if (_typeof(table.options.responsiveLayout) && table.extExists("responsiveLayout")) {
table.extensions.responsiveLayout.initializeColumn(self);
}
//set column visibility
if (typeof def.visible != "undefined") {
if (def.visible) {
self.show(true);
} else {
self.hide(true);
}
}
//asign additional css classes to column header
if (def.cssClass) {
self.element.addClass(def.cssClass);
}
if (def.field) {
this.element.attr("tabulator-field", def.field);
}
//set min width if present
self.setMinWidth(typeof def.minWidth == "undefined" ? self.table.options.columnMinWidth : def.minWidth);
self.reinitializeWidth();
//set tooltip if present
self.tooltip = self.definition.tooltip || self.definition.tooltip === false ? self.definition.tooltip : self.table.options.tooltips;
//set orizontal text alignment
self.hozAlign = typeof self.definition.align == "undefined" ? "" : self.definition.align;
};
Column.prototype._buildColumnHeaderContent = function () {
var self = this,
def = self.definition,
table = self.table;
var contentElement = $("<div class='tabulator-col-content'></div>");
contentElement.append(self._buildColumnHeaderTitle());
return contentElement;
};
//build title element of column
Column.prototype._buildColumnHeaderTitle = function () {
var self = this,
def = self.definition,
table = self.table,
title;
var titleHolderElement = $("<div class='tabulator-col-title'></div>");
if (def.editableTitle) {
var titleElement = $("<input class='tabulator-title-editor'>");
titleElement.on("click", function (e) {
e.stopPropagation();
$(this).focus();
});
titleElement.on("change", function () {
var newTitle = $(this).val();
def.title = newTitle;
table.options.columnTitleChanged(self.getComponent());
});
titleHolderElement.append(titleElement);
if (def.field) {
table.extensions.localize.bind("columns|" + def.field, function (text) {
titleElement.val(text || def.title || " ");
});
} else {
titleElement.val(def.title || " ");
}
} else {
if (def.field) {
table.extensions.localize.bind("columns|" + def.field, function (text) {
self._formatColumnHeaderTitle(titleHolderElement, text || def.title || " ");
});
} else {
self._formatColumnHeaderTitle(titleHolderElement, def.title || " ");
}
}
return titleHolderElement;
};
Column.prototype._formatColumnHeaderTitle = function (el, title) {
var formatter, contents;
if (this.definition.titleFormatter && this.table.extExists("format")) {
formatter = this.table.extensions.format.getFormatter(this.definition.titleFormatter);
contents = formatter.call(this.table.extensions.format, {
getValue: function getValue() {
return title;
},
getElement: function getElement() {
return el;
}
}, this.definition.titleFormatterParams || {});
el.append(contents);
} else {
el.html(title);
}
};
//build header element for column group
Column.prototype._buildGroupHeader = function () {
var self = this,
def = self.definition,
table = self.table;
self.element.addClass("tabulator-col-group").attr("role", "columngroup").attr("aria-title", def.title);
self.element.append(self.groupElement);
};
//flat field lookup
Column.prototype._getFlatData = function (data) {
return data[this.field];
};
//nested field lookup
Column.prototype._getNestedData = function (data) {
var dataObj = data,
structure = this.fieldStructure,
length = structure.length,
output;
for (var i = 0; i < length; i++) {
dataObj = dataObj[structure[i]];
output = dataObj;
if (!dataObj) {
break;
}
}
return output;
};
//flat field set
Column.prototype._setFlatData = function (data, value) {
data[this.field] = value;
};
//nested field set
Column.prototype._setNesteData = function (data, value) {
var dataObj = data,
structure = this.fieldStructure,
length = structure.length;
for (var i = 0; i < length; i++) {
if (i == length - 1) {
dataObj[structure[i]] = value;
} else {
if (!dataObj[structure[i]]) {
dataObj[structure[i]] = {};
}
dataObj = dataObj[structure[i]];
}
}
};
//attach column to this group
Column.prototype.attachColumn = function (column) {
var self = this;
if (self.groupElement) {
self.columns.push(column);
self.groupElement.append(column.getElement());
} else {
console.warn("Column Warning - Column being attached to another column instead of column group");
}
};
//vertically align header in column
Column.prototype.verticalAlign = function (alignment) {
//calculate height of column header and group holder element
var parentHeight = this.parent.isGroup ? this.parent.getGroupElement().innerHeight() : this.parent.getHeadersElement().innerHeight();
this.element.css("height", parentHeight);
if (this.isGroup) {
this.groupElement.css("min-height", parentHeight - this.contentElement.outerHeight());
}
//vertically align cell contents
if (!this.isGroup && alignment !== "top") {
if (alignment === "bottom") {
this.element.css({ "padding-top": this.element.innerHeight() - this.contentElement.outerHeight() });
} else {
this.element.css({ "padding-top": (this.element.innerHeight() - this.contentElement.outerHeight()) / 2 });
}
}
this.columns.forEach(function (column) {
column.verticalAlign(alignment);
});
};
//clear vertical alignmenet
Column.prototype.clearVerticalAlign = function () {
this.element.css("padding-top", "");
this.element.css("height", "");
this.element.css("min-height", "");
this.columns.forEach(function (column) {
column.clearVerticalAlign();
});
};
//// Retreive Column Information ////
//return column header element
Column.prototype.getElement = function () {
return this.element;
};
//return colunm group element
Column.prototype.getGroupElement = function () {
return this.groupElement;
};
//return field name
Column.prototype.getField = function () {
return this.field;
};
//return the first column in a group
Column.prototype.getFirstColumn = function () {
if (!this.isGroup) {
return this;
} else {
if (this.columns.length) {
return this.columns[0].getFirstColumn();
} else {
return false;
}
}
};
//return the last column in a group
Column.prototype.getLastColumn = function () {
if (!this.isGroup) {
return this;
} else {
if (this.columns.length) {
return this.columns[this.columns.length - 1].getLastColumn();
} else {
return false;
}
}
};
//return all columns in a group
Column.prototype.getColumns = function () {
return this.columns;
};
//return all columns in a group
Column.prototype.getCells = function () {
return this.cells;
};
//retreive the top column in a group of columns
Column.prototype.getTopColumn = function () {
if (this.parent.isGroup) {
return this.parent.getTopColumn();
} else {
return this;
}
};
//return column definition object
Column.prototype.getDefinition = function (updateBranches) {
var colDefs = [];
if (this.isGroup && updateBranches) {
this.columns.forEach(function (column) {
colDefs.push(column.getDefinition(true));
});
this.definition.columns = colDefs;
}
return this.definition;
};
//////////////////// Actions ////////////////////
Column.prototype.checkColumnVisibility = function () {
var visible = false;
this.columns.forEach(function (column) {
if (column.visible) {
visible = true;
}
});
if (visible) {
this.show();
this.parent.table.options.columnVisibilityChanged(this.getComponent(), false);
} else {
this.hide();
}
};
//show column
Column.prototype.show = function (silent, responsiveToggle) {
if (!this.visible) {
this.visible = true;
this.element.css({
"display": ""
});
this.table.columnManager._verticalAlignHeaders();
if (this.parent.isGroup) {
this.parent.checkColumnVisibility();
}
this.cells.forEach(function (cell) {
cell.show();
});
if (this.table.options.persistentLayout && this.table.extExists("responsiveLayout", true)) {
this.table.extensions.persistence.save("columns");
}
if (!responsiveToggle && this.table.options.responsiveLayout && this.table.extExists("responsiveLayout", true)) {
this.table.extensions.responsiveLayout.updateColumnVisibility(this, this.visible);
}
if (!silent) {
this.table.options.columnVisibilityChanged(this.getComponent(), true);
}
}
};
//hide column
Column.prototype.hide = function (silent, responsiveToggle) {
if (this.visible) {
this.visible = false;
this.element.css({
"display": "none"
});
this.table.columnManager._verticalAlignHeaders();
if (this.parent.isGroup) {
this.parent.checkColumnVisibility();
}
this.cells.forEach(function (cell) {
cell.hide();
});
if (this.table.options.persistentLayout && this.table.extExists("persistence", true)) {
this.table.extensions.persistence.save("columns");
}
if (!responsiveToggle && this.table.options.responsiveLayout && this.table.extExists("responsiveLayout", true)) {
this.table.extensions.responsiveLayout.updateColumnVisibility(this, this.visible);
}
if (!silent) {
this.table.options.columnVisibilityChanged(this.getComponent(), false);
}
}
};
Column.prototype.matchChildWidths = function () {
var childWidth = 0;
if (this.contentElement && this.columns.length) {
this.columns.forEach(function (column) {
childWidth += column.getWidth();
});
this.contentElement.css("max-width", childWidth - 1);
}
};
Column.prototype.setWidth = function (width) {
this.widthFixed = true;
this.setWidthActual(width);
};
Column.prototype.setWidthActual = function (width) {
if (isNaN(width)) {
width = Math.floor(this.table.element.innerWidth() / 100 * parseInt(width));
}
width = Math.max(this.minWidth, width);
this.width = width;
this.element.css("width", width || "");
if (!this.isGroup) {
this.cells.forEach(function (cell) {
cell.setWidth(width);
});
}
if (this.parent.isGroup) {
this.parent.matchChildWidths();
}
//set resizable handles
if (this.table.extExists("frozenColumns")) {
this.table.extensions.frozenColumns.layout();
}
};
Column.prototype.checkCellHeights = function () {
var rows = [];
this.cells.forEach(function (cell) {
if (cell.row.heightInitialized) {
if (cell.row.element[0].offsetParent !== null) {
rows.push(cell.row);
cell.row.clearCellHeight();
} else {
cell.row.heightInitialized = false;
}
}
});
rows.forEach(function (row) {
row.calcHeight();
});
rows.forEach(function (row) {
row.setCellHeight();
});
};
Column.prototype.getWidth = function () {
return this.element.outerWidth();
};
Column.prototype.getHeight = function () {
return this.element.outerHeight();
};
Column.prototype.setMinWidth = function (minWidth) {
this.minWidth = minWidth;
this.element.css("min-width", minWidth || "");
this.cells.forEach(function (cell) {
cell.setMinWidth(minWidth);
});
};
Column.prototype.delete = function () {
if (this.isGroup) {
this.columns.forEach(function (column) {
column.delete();
});
}
var cellCount = this.cells.length;
for (var i = 0; i < cellCount; i++) {
this.cells[0].delete();
}
this.element.detach();
this.table.columnManager.deregisterColumn(this);
};
//////////////// Cell Management /////////////////
//generate cell for this column
Column.prototype.generateCell = function (row) {
var self = this;
var cell = new Cell(self, row);
this.cells.push(cell);
return cell;
};
Column.prototype.reinitializeWidth = function (force) {
this.widthFixed = false;
//set width if present
if (typeof this.definition.width !== "undefined" && !force) {
this.setWidth(this.definition.width);
}
//hide header filters to prevent them altering column width
if (this.table.extExists("filter")) {
this.table.extensions.filter.hideHeaderFilterElements();
}
this.fitToData();
//show header filters again after layout is complete
if (this.table.extExists("filter")) {
this.table.extensions.filter.showHeaderFilterElements();
}
};
//set column width to maximum cell width
Column.prototype.fitToData = function () {
var self = this;
if (!this.widthFixed) {
this.element.css("width", "");
self.cells.forEach(function (cell) {
cell.setWidth("");
});
}
var maxWidth = this.element.outerWidth();
if (!self.width || !this.widthFixed) {
self.cells.forEach(function (cell) {
var width = cell.getWidth();
if (width > maxWidth) {
maxWidth = width;
}
});
if (maxWidth) {
self.setWidthActual(maxWidth + 1);
}
}
};
Column.prototype.deleteCell = function (cell) {
var index = this.cells.indexOf(cell);
if (index > -1) {
this.cells.splice(index, 1);
}
};
//////////////// Event Bindings /////////////////
//////////////// Object Generation /////////////////
Column.prototype.getComponent = function () {
return new ColumnComponent(this);
};
var RowManager = function RowManager(table) {
this.table = table;
this.element = $("<div class='tabulator-tableHolder' tabindex='0'></div>"); //containing element
this.tableElement = $("<div class='tabulator-table'></div>"); //table element
this.columnManager = null; //hold column manager object
this.height = 0; //hold height of table element
this.firstRender = false; //handle first render
this.renderMode = "classic"; //current rendering mode
this.rows = []; //hold row data objects
this.activeRows = []; //rows currently available to on display in the table
this.activeRowsCount = 0; //count of active rows
this.displayRows = []; //rows currently on display in the table
this.displayRowsCount = 0; //count of display rows
this.scrollTop = 0;
this.scrollLeft = 0;
this.vDomRowHeight = 20; //approximation of row heights for padding
this.vDomTop = 0; //hold position for first rendered row in the virtual DOM
this.vDomBottom = 0; //hold possition for last rendered row in the virtual DOM
this.vDomScrollPosTop = 0; //last scroll position of the vDom top;
this.vDomScrollPosBottom = 0; //last scroll position of the vDom bottom;
this.vDomTopPad = 0; //hold value of padding for top of virtual DOM
this.vDomBottomPad = 0; //hold value of padding for bottom of virtual DOM
this.vDomMaxRenderChain = 90; //the maximum number of dom elements that can be rendered in 1 go
this.vDomWindowBuffer = 0; //window row buffer before removing elements, to smooth scrolling
this.vDomWindowMinTotalRows = 20; //minimum number of rows to be generated in virtual dom (prevent buffering issues on tables with tall rows)
this.vDomWindowMinMarginRows = 5; //minimum number of rows to be generated in virtual dom margin
this.vDomTopNewRows = []; //rows to normalize after appending to optimize render speed
this.vDomBottomNewRows = []; //rows to normalize after appending to optimize render speed
};
//////////////// Setup Functions /////////////////
//return containing element
RowManager.prototype.getElement = function () {
return this.element;
};
//return table element
RowManager.prototype.getTableElement = function () {
return this.tableElement;
};
//return position of row in table
RowManager.prototype.getRowPosition = function (row, active) {
if (active) {
return this.activeRows.indexOf(row);
} else {
return this.rows.indexOf(row);
}
};
//link to column manager
RowManager.prototype.setColumnManager = function (manager) {
this.columnManager = manager;
};
RowManager.prototype.initialize = function () {
var self = this;
self.setRenderMode();
//initialize manager
self.element.append(self.tableElement);
self.firstRender = true