/* Tabulator v5.4.4 (c) Oliver Folkerd 2023 */
class CoreFeature{
constructor(table){
this.table = table;
}
//////////////////////////////////////////
/////////////// DataLoad /////////////////
//////////////////////////////////////////
reloadData(data, silent, columnsChanged){
return this.table.dataLoader.load(data, undefined, undefined, undefined, silent, columnsChanged);
}
//////////////////////////////////////////
///////////// Localization ///////////////
//////////////////////////////////////////
langText(){
return this.table.modules.localize.getText(...arguments);
}
langBind(){
return this.table.modules.localize.bind(...arguments);
}
langLocale(){
return this.table.modules.localize.getLocale(...arguments);
}
//////////////////////////////////////////
////////// Inter Table Comms /////////////
//////////////////////////////////////////
commsConnections(){
return this.table.modules.comms.getConnections(...arguments);
}
commsSend(){
return this.table.modules.comms.send(...arguments);
}
//////////////////////////////////////////
//////////////// Layout /////////////////
//////////////////////////////////////////
layoutMode(){
return this.table.modules.layout.getMode();
}
layoutRefresh(force){
return this.table.modules.layout.layout(force);
}
//////////////////////////////////////////
/////////////// Event Bus ////////////////
//////////////////////////////////////////
subscribe(){
return this.table.eventBus.subscribe(...arguments);
}
unsubscribe(){
return this.table.eventBus.unsubscribe(...arguments);
}
subscribed(key){
return this.table.eventBus.subscribed(key);
}
subscriptionChange(){
return this.table.eventBus.subscriptionChange(...arguments);
}
dispatch(){
return this.table.eventBus.dispatch(...arguments);
}
chain(){
return this.table.eventBus.chain(...arguments);
}
confirm(){
return this.table.eventBus.confirm(...arguments);
}
dispatchExternal(){
return this.table.externalEvents.dispatch(...arguments);
}
subscribedExternal(key){
return this.table.externalEvents.subscribed(key);
}
subscriptionChangeExternal(){
return this.table.externalEvents.subscriptionChange(...arguments);
}
//////////////////////////////////////////
//////////////// Options /////////////////
//////////////////////////////////////////
options(key){
return this.table.options[key];
}
setOption(key, value){
if(typeof value !== "undefined"){
this.table.options[key] = value;
}
return this.table.options[key];
}
//////////////////////////////////////////
/////////// Deprecation Checks ///////////
//////////////////////////////////////////
deprecationCheck(oldOption, newOption){
return this.table.deprecationAdvisor.check(oldOption, newOption);
}
deprecationCheckMsg(oldOption, msg){
return this.table.deprecationAdvisor.checkMsg(oldOption, msg);
}
deprecationMsg(msg){
return this.table.deprecationAdvisor.msg(msg);
}
//////////////////////////////////////////
//////////////// Modules /////////////////
//////////////////////////////////////////
module(key){
return this.table.module(key);
}
}
class Helpers{
static elVisible(el){
return !(el.offsetWidth <= 0 && el.offsetHeight <= 0);
}
static elOffset(el){
var box = el.getBoundingClientRect();
return {
top: box.top + window.pageYOffset - document.documentElement.clientTop,
left: box.left + window.pageXOffset - document.documentElement.clientLeft
};
}
static deepClone(obj, clone, list = []){
var objectProto = {}.__proto__,
arrayProto = [].__proto__;
if (!clone){
clone = Object.assign(Array.isArray(obj) ? [] : {}, obj);
}
for(var i in obj) {
let subject = obj[i],
match, copy;
if(subject != null && typeof subject === "object" && (subject.__proto__ === objectProto || subject.__proto__ === arrayProto)){
match = list.findIndex((item) => {
return item.subject === subject;
});
if(match > -1){
clone[i] = list[match].copy;
}else {
copy = Object.assign(Array.isArray(subject) ? [] : {}, subject);
list.unshift({subject, copy});
clone[i] = this.deepClone(subject, copy, list);
}
}
}
return clone;
}
}
class Popup extends CoreFeature{
constructor(table, element, parent){
super(table);
this.element = element;
this.container = this._lookupContainer();
this.parent = parent;
this.reversedX = false;
this.childPopup = null;
this.blurable = false;
this.blurCallback = null;
this.blurEventsBound = false;
this.renderedCallback = null;
this.visible = false;
this.hideable = true;
this.element.classList.add("tabulator-popup-container");
this.blurEvent = this.hide.bind(this, false);
this.escEvent = this._escapeCheck.bind(this);
this.destroyBinding = this.tableDestroyed.bind(this);
this.destroyed = false;
}
tableDestroyed(){
this.destroyed = true;
this.hide(true);
}
_lookupContainer(){
var container = this.table.options.popupContainer;
if(typeof container === "string"){
container = document.querySelector(container);
if(!container){
console.warn("Menu Error - no container element found matching selector:", this.table.options.popupContainer , "(defaulting to document body)");
}
}else if (container === true){
container = this.table.element;
}
if(container && !this._checkContainerIsParent(container)){
container = false;
console.warn("Menu Error - container element does not contain this table:", this.table.options.popupContainer , "(defaulting to document body)");
}
if(!container){
container = document.body;
}
return container;
}
_checkContainerIsParent(container, element = this.table.element){
if(container === element){
return true;
}else {
return element.parentNode ? this._checkContainerIsParent(container, element.parentNode) : false;
}
}
renderCallback(callback){
this.renderedCallback = callback;
}
containerEventCoords(e){
var touch = !(e instanceof MouseEvent);
var x = touch ? e.touches[0].pageX : e.pageX;
var y = touch ? e.touches[0].pageY : e.pageY;
if(this.container !== document.body){
let parentOffset = Helpers.elOffset(this.container);
x -= parentOffset.left;
y -= parentOffset.top;
}
return {x, y};
}
elementPositionCoords(element, position = "right"){
var offset = Helpers.elOffset(element),
containerOffset, x, y;
if(this.container !== document.body){
containerOffset = Helpers.elOffset(this.container);
offset.left -= containerOffset.left;
offset.top -= containerOffset.top;
}
switch(position){
case "right":
x = offset.left + element.offsetWidth;
y = offset.top - 1;
break;
case "bottom":
x = offset.left;
y = offset.top + element.offsetHeight;
break;
case "left":
x = offset.left;
y = offset.top - 1;
break;
case "top":
x = offset.left;
y = offset.top;
break;
case "center":
x = offset.left + (element.offsetWidth / 2);
y = offset.top + (element.offsetHeight / 2);
break;
}
return {x, y, offset};
}
show(origin, position){
var x, y, parentEl, parentOffset, coords;
if(this.destroyed || this.table.destroyed){
return this;
}
if(origin instanceof HTMLElement){
parentEl = origin;
coords = this.elementPositionCoords(origin, position);
parentOffset = coords.offset;
x = coords.x;
y = coords.y;
}else if(typeof origin === "number"){
parentOffset = {top:0, left:0};
x = origin;
y = position;
}else {
coords = this.containerEventCoords(origin);
x = coords.x;
y = coords.y;
this.reversedX = false;
}
this.element.style.top = y + "px";
this.element.style.left = x + "px";
this.container.appendChild(this.element);
if(typeof this.renderedCallback === "function"){
this.renderedCallback();
}
this._fitToScreen(x, y, parentEl, parentOffset, position);
this.visible = true;
this.subscribe("table-destroy", this.destroyBinding);
this.element.addEventListener("mousedown", (e) => {
e.stopPropagation();
});
return this;
}
_fitToScreen(x, y, parentEl, parentOffset, position){
var scrollTop = this.container === document.body ? document.documentElement.scrollTop : this.container.scrollTop;
//move menu to start on right edge if it is too close to the edge of the screen
if((x + this.element.offsetWidth) >= this.container.offsetWidth || this.reversedX){
this.element.style.left = "";
if(parentEl){
this.element.style.right = (this.container.offsetWidth - parentOffset.left) + "px";
}else {
this.element.style.right = (this.container.offsetWidth - x) + "px";
}
this.reversedX = true;
}
//move menu to start on bottom edge if it is too close to the edge of the screen
if((y + this.element.offsetHeight) > Math.max(this.container.offsetHeight, scrollTop ? this.container.scrollHeight : 0)) {
if(parentEl){
switch(position){
case "bottom":
this.element.style.top = (parseInt(this.element.style.top) - this.element.offsetHeight - parentEl.offsetHeight - 1) + "px";
break;
default:
this.element.style.top = (parseInt(this.element.style.top) - this.element.offsetHeight + parentEl.offsetHeight + 1) + "px";
}
}else {
this.element.style.top = (parseInt(this.element.style.top) - this.element.offsetHeight) + "px";
}
}
}
isVisible(){
return this.visible;
}
hideOnBlur(callback){
this.blurable = true;
if(this.visible){
setTimeout(() => {
if(this.visible){
this.table.rowManager.element.addEventListener("scroll", this.blurEvent);
this.subscribe("cell-editing", this.blurEvent);
document.body.addEventListener("click", this.blurEvent);
document.body.addEventListener("contextmenu", this.blurEvent);
document.body.addEventListener("mousedown", this.blurEvent);
window.addEventListener("resize", this.blurEvent);
document.body.addEventListener("keydown", this.escEvent);
this.blurEventsBound = true;
}
}, 100);
this.blurCallback = callback;
}
return this;
}
_escapeCheck(e){
if(e.keyCode == 27){
this.hide();
}
}
blockHide(){
this.hideable = false;
}
restoreHide(){
this.hideable = true;
}
hide(silent = false){
if(this.visible && this.hideable){
if(this.blurable && this.blurEventsBound){
document.body.removeEventListener("keydown", this.escEvent);
document.body.removeEventListener("click", this.blurEvent);
document.body.removeEventListener("contextmenu", this.blurEvent);
document.body.removeEventListener("mousedown", this.blurEvent);
window.removeEventListener("resize", this.blurEvent);
this.table.rowManager.element.removeEventListener("scroll", this.blurEvent);
this.unsubscribe("cell-editing", this.blurEvent);
this.blurEventsBound = false;
}
if(this.childPopup){
this.childPopup.hide();
}
if(this.parent){
this.parent.childPopup = null;
}
if(this.element.parentNode){
this.element.parentNode.removeChild(this.element);
}
this.visible = false;
if(this.blurCallback && !silent){
this.blurCallback();
}
this.unsubscribe("table-destroy", this.destroyBinding);
}
return this;
}
child(element){
if(this.childPopup){
this.childPopup.hide();
}
this.childPopup = new Popup(this.table, element, this);
return this.childPopup;
}
}
class Module extends CoreFeature{
constructor(table, name){
super(table);
this._handler = null;
}
initialize(){
// setup module when table is initialized, to be overridden in module
}
///////////////////////////////////
////// Options Registration ///////
///////////////////////////////////
registerTableOption(key, value){
this.table.optionsList.register(key, value);
}
registerColumnOption(key, value){
this.table.columnManager.optionsList.register(key, value);
}
///////////////////////////////////
/// Public Function Registration ///
///////////////////////////////////
registerTableFunction(name, func){
if(typeof this.table[name] === "undefined"){
this.table[name] = (...args) => {
this.table.initGuard(name);
return func(...args);
};
}else {
console.warn("Unable to bind table function, name already in use", name);
}
}
registerComponentFunction(component, func, handler){
return this.table.componentFunctionBinder.bind(component, func, handler);
}
///////////////////////////////////
////////// Data Pipeline //////////
///////////////////////////////////
registerDataHandler(handler, priority){
this.table.rowManager.registerDataPipelineHandler(handler, priority);
this._handler = handler;
}
registerDisplayHandler(handler, priority){
this.table.rowManager.registerDisplayPipelineHandler(handler, priority);
this._handler = handler;
}
displayRows(adjust){
var index = this.table.rowManager.displayRows.length - 1,
lookupIndex;
if(this._handler){
lookupIndex = this.table.rowManager.displayPipeline.findIndex((item) => {
return item.handler === this._handler;
});
if(lookupIndex > -1){
index = lookupIndex;
}
}
if(adjust){
index = index + adjust;
}
if(this._handler){
if(index > -1){
return this.table.rowManager.getDisplayRows(index);
}else {
return this.activeRows();
}
}
}
activeRows(){
return this.table.rowManager.activeRows;
}
refreshData(renderInPosition, handler){
if(!handler){
handler = this._handler;
}
if(handler){
this.table.rowManager.refreshActiveData(handler, false, renderInPosition);
}
}
///////////////////////////////////
//////// Footer Management ////////
///////////////////////////////////
footerAppend(element){
return this.table.footerManager.append(element);
}
footerPrepend(element){
return this.table.footerManager.prepend(element);
}
footerRemove(element){
return this.table.footerManager.remove(element);
}
///////////////////////////////////
//////// Popups Management ////////
///////////////////////////////////
popup(menuEl, menuContainer){
return new Popup(this.table, menuEl, menuContainer);
}
///////////////////////////////////
//////// Alert Management ////////
///////////////////////////////////
alert(content, type){
return this.table.alertManager.alert(content, type);
}
clearAlert(){
return this.table.alertManager.clear();
}
}
var defaultAccessors = {};
class Accessor extends Module{
constructor(table){
super(table);
this.allowedTypes = ["", "data", "download", "clipboard", "print", "htmlOutput"]; //list of accessor types
this.registerColumnOption("accessor");
this.registerColumnOption("accessorParams");
this.registerColumnOption("accessorData");
this.registerColumnOption("accessorDataParams");
this.registerColumnOption("accessorDownload");
this.registerColumnOption("accessorDownloadParams");
this.registerColumnOption("accessorClipboard");
this.registerColumnOption("accessorClipboardParams");
this.registerColumnOption("accessorPrint");
this.registerColumnOption("accessorPrintParams");
this.registerColumnOption("accessorHtmlOutput");
this.registerColumnOption("accessorHtmlOutputParams");
}
initialize(){
this.subscribe("column-layout", this.initializeColumn.bind(this));
this.subscribe("row-data-retrieve", this.transformRow.bind(this));
}
//initialize column accessor
initializeColumn(column){
var match = false,
config = {};
this.allowedTypes.forEach((type) => {
var key = "accessor" + (type.charAt(0).toUpperCase() + type.slice(1)),
accessor;
if(column.definition[key]){
accessor = this.lookupAccessor(column.definition[key]);
if(accessor){
match = true;
config[key] = {
accessor:accessor,
params: column.definition[key + "Params"] || {},
};
}
}
});
if(match){
column.modules.accessor = config;
}
}
lookupAccessor(value){
var accessor = false;
//set column accessor
switch(typeof value){
case "string":
if(Accessor.accessors[value]){
accessor = Accessor.accessors[value];
}else {
console.warn("Accessor Error - No such accessor found, ignoring: ", value);
}
break;
case "function":
accessor = value;
break;
}
return accessor;
}
//apply accessor to row
transformRow(row, type){
var key = "accessor" + (type.charAt(0).toUpperCase() + type.slice(1)),
rowComponent = row.getComponent();
//clone data object with deep copy to isolate internal data from returned result
var data = Helpers.deepClone(row.data || {});
this.table.columnManager.traverse(function(column){
var value, accessor, params, colComponent;
if(column.modules.accessor){
accessor = column.modules.accessor[key] || column.modules.accessor.accessor || false;
if(accessor){
value = column.getFieldValue(data);
if(value != "undefined"){
colComponent = column.getComponent();
params = typeof accessor.params === "function" ? accessor.params(value, data, type, colComponent, rowComponent) : accessor.params;
column.setFieldValue(data, accessor.accessor(value, data, type, params, colComponent, rowComponent));
}
}
}
});
return data;
}
}
//load defaults
Accessor.moduleName = "accessor";
Accessor.accessors = defaultAccessors;
var defaultConfig = {
method: "GET",
};
function generateParamsList(data, prefix){
var output = [];
prefix = prefix || "";
if(Array.isArray(data)){
data.forEach((item, i) => {
output = output.concat(generateParamsList(item, prefix ? prefix + "[" + i + "]" : i));
});
}else if (typeof data === "object"){
for (var key in data){
output = output.concat(generateParamsList(data[key], prefix ? prefix + "[" + key + "]" : key));
}
}else {
output.push({key:prefix, value:data});
}
return output;
}
function serializeParams(params){
var output = generateParamsList(params),
encoded = [];
output.forEach(function(item){
encoded.push(encodeURIComponent(item.key) + "=" + encodeURIComponent(item.value));
});
return encoded.join("&");
}
function urlBuilder(url, config, params){
if(url){
if(params && Object.keys(params).length){
if(!config.method || config.method.toLowerCase() == "get"){
config.method = "get";
url += (url.includes("?") ? "&" : "?") + serializeParams(params);
}
}
}
return url;
}
function defaultLoaderPromise(url, config, params){
var contentType;
return new Promise((resolve, reject) => {
//set url
url = this.urlGenerator.call(this.table, url, config, params);
//set body content if not GET request
if(config.method.toUpperCase() != "GET"){
contentType = typeof this.table.options.ajaxContentType === "object" ? this.table.options.ajaxContentType : this.contentTypeFormatters[this.table.options.ajaxContentType];
if(contentType){
for(var key in contentType.headers){
if(!config.headers){
config.headers = {};
}
if(typeof config.headers[key] === "undefined"){
config.headers[key] = contentType.headers[key];
}
}
config.body = contentType.body.call(this, url, config, params);
}else {
console.warn("Ajax Error - Invalid ajaxContentType value:", this.table.options.ajaxContentType);
}
}
if(url){
//configure headers
if(typeof config.headers === "undefined"){
config.headers = {};
}
if(typeof config.headers.Accept === "undefined"){
config.headers.Accept = "application/json";
}
if(typeof config.headers["X-Requested-With"] === "undefined"){
config.headers["X-Requested-With"] = "XMLHttpRequest";
}
if(typeof config.mode === "undefined"){
config.mode = "cors";
}
if(config.mode == "cors"){
if(typeof config.headers["Origin"] === "undefined"){
config.headers["Origin"] = window.location.origin;
}
if(typeof config.credentials === "undefined"){
config.credentials = 'same-origin';
}
}else {
if(typeof config.credentials === "undefined"){
config.credentials = 'include';
}
}
//send request
fetch(url, config)
.then((response)=>{
if(response.ok) {
response.json()
.then((data)=>{
resolve(data);
}).catch((error)=>{
reject(error);
console.warn("Ajax Load Error - Invalid JSON returned", error);
});
}else {
console.error("Ajax Load Error - Connection Error: " + response.status, response.statusText);
reject(response);
}
})
.catch((error)=>{
console.error("Ajax Load Error - Connection Error: ", error);
reject(error);
});
}else {
console.warn("Ajax Load Error - No URL Set");
resolve([]);
}
});
}
function generateParamsList$1(data, prefix){
var output = [];
prefix = prefix || "";
if(Array.isArray(data)){
data.forEach((item, i) => {
output = output.concat(generateParamsList$1(item, prefix ? prefix + "[" + i + "]" : i));
});
}else if (typeof data === "object"){
for (var key in data){
output = output.concat(generateParamsList$1(data[key], prefix ? prefix + "[" + key + "]" : key));
}
}else {
output.push({key:prefix, value:data});
}
return output;
}
var defaultContentTypeFormatters = {
"json":{
headers:{
'Content-Type': 'application/json',
},
body:function(url, config, params){
return JSON.stringify(params);
},
},
"form":{
headers:{
},
body:function(url, config, params){
var output = generateParamsList$1(params),
form = new FormData();
output.forEach(function(item){
form.append(item.key, item.value);
});
return form;
},
},
};
class Ajax extends Module{
constructor(table){
super(table);
this.config = {}; //hold config object for ajax request
this.url = ""; //request URL
this.urlGenerator = false;
this.params = false; //request parameters
this.loaderPromise = false;
this.registerTableOption("ajaxURL", false); //url for ajax loading
this.registerTableOption("ajaxURLGenerator", false);
this.registerTableOption("ajaxParams", {}); //params for ajax loading
this.registerTableOption("ajaxConfig", "get"); //ajax request type
this.registerTableOption("ajaxContentType", "form"); //ajax request type
this.registerTableOption("ajaxRequestFunc", false); //promise function
this.registerTableOption("ajaxRequesting", function(){});
this.registerTableOption("ajaxResponse", false);
this.contentTypeFormatters = Ajax.contentTypeFormatters;
}
//initialize setup options
initialize(){
this.loaderPromise = this.table.options.ajaxRequestFunc || Ajax.defaultLoaderPromise;
this.urlGenerator = this.table.options.ajaxURLGenerator || Ajax.defaultURLGenerator;
if(this.table.options.ajaxURL){
this.setUrl(this.table.options.ajaxURL);
}
this.setDefaultConfig(this.table.options.ajaxConfig);
this.registerTableFunction("getAjaxUrl", this.getUrl.bind(this));
this.subscribe("data-loading", this.requestDataCheck.bind(this));
this.subscribe("data-params", this.requestParams.bind(this));
this.subscribe("data-load", this.requestData.bind(this));
}
requestParams(data, config, silent, params){
var ajaxParams = this.table.options.ajaxParams;
if(ajaxParams){
if(typeof ajaxParams === "function"){
ajaxParams = ajaxParams.call(this.table);
}
params = Object.assign(params, ajaxParams);
}
return params;
}
requestDataCheck(data, params, config, silent){
return !!((!data && this.url) || typeof data === "string");
}
requestData(url, params, config, silent, previousData){
var ajaxConfig;
if(!previousData && this.requestDataCheck(url)){
if(url){
this.setUrl(url);
}
ajaxConfig = this.generateConfig(config);
return this.sendRequest(this.url, params, ajaxConfig);
}else {
return previousData;
}
}
setDefaultConfig(config = {}){
this.config = Object.assign({}, Ajax.defaultConfig);
if(typeof config == "string"){
this.config.method = config;
}else {
Object.assign(this.config, config);
}
}
//load config object
generateConfig(config = {}){
var ajaxConfig = Object.assign({}, this.config);
if(typeof config == "string"){
ajaxConfig.method = config;
}else {
Object.assign(ajaxConfig, config);
}
return ajaxConfig;
}
//set request url
setUrl(url){
this.url = url;
}
//get request url
getUrl(){
return this.url;
}
//send ajax request
sendRequest(url, params, config){
if(this.table.options.ajaxRequesting.call(this.table, url, params) !== false){
return this.loaderPromise(url, config, params)
.then((data)=>{
if(this.table.options.ajaxResponse){
data = this.table.options.ajaxResponse.call(this.table, url, params, data);
}
return data;
});
}else {
return Promise.reject();
}
}
}
Ajax.moduleName = "ajax";
//load defaults
Ajax.defaultConfig = defaultConfig;
Ajax.defaultURLGenerator = urlBuilder;
Ajax.defaultLoaderPromise = defaultLoaderPromise;
Ajax.contentTypeFormatters = defaultContentTypeFormatters;
var defaultPasteActions = {
replace:function(rows){
return this.table.setData(rows);
},
update:function(rows){
return this.table.updateOrAddData(rows);
},
insert:function(rows){
return this.table.addData(rows);
},
};
var defaultPasteParsers = {
table:function(clipboard){
var data = [],
headerFindSuccess = true,
columns = this.table.columnManager.columns,
columnMap = [],
rows = [];
//get data from clipboard into array of columns and rows.
clipboard = clipboard.split("\n");
clipboard.forEach(function(row){
data.push(row.split("\t"));
});
if(data.length && !(data.length === 1 && data[0].length < 2)){
//check if headers are present by title
data[0].forEach(function(value){
var column = columns.find(function(column){
return value && column.definition.title && value.trim() && column.definition.title.trim() === value.trim();
});
if(column){
columnMap.push(column);
}else {
headerFindSuccess = false;
}
});
//check if column headers are present by field
if(!headerFindSuccess){
headerFindSuccess = true;
columnMap = [];
data[0].forEach(function(value){
var column = columns.find(function(column){
return value && column.field && value.trim() && column.field.trim() === value.trim();
});
if(column){
columnMap.push(column);
}else {
headerFindSuccess = false;
}
});
if(!headerFindSuccess){
columnMap = this.table.columnManager.columnsByIndex;
}
}
//remove header row if found
if(headerFindSuccess){
data.shift();
}
data.forEach(function(item){
var row = {};
item.forEach(function(value, i){
if(columnMap[i]){
row[columnMap[i].field] = value;
}
});
rows.push(row);
});
return rows;
}else {
return false;
}
}
};
class Clipboard extends Module{
constructor(table){
super(table);
this.mode = true;
this.pasteParser = function(){};
this.pasteAction = function(){};
this.customSelection = false;
this.rowRange = false;
this.blocked = true; //block copy actions not originating from this command
this.registerTableOption("clipboard", false); //enable clipboard
this.registerTableOption("clipboardCopyStyled", true); //formatted table data
this.registerTableOption("clipboardCopyConfig", false); //clipboard config
this.registerTableOption("clipboardCopyFormatter", false); //DEPRECATED - REMOVE in 5.0
this.registerTableOption("clipboardCopyRowRange", "active"); //restrict clipboard to visible rows only
this.registerTableOption("clipboardPasteParser", "table"); //convert pasted clipboard data to rows
this.registerTableOption("clipboardPasteAction", "insert"); //how to insert pasted data into the table
this.registerColumnOption("clipboard");
this.registerColumnOption("titleClipboard");
}
initialize(){
this.mode = this.table.options.clipboard;
this.rowRange = this.table.options.clipboardCopyRowRange;
if(this.mode === true || this.mode === "copy"){
this.table.element.addEventListener("copy", (e) => {
var plain, html, list;
if(!this.blocked){
e.preventDefault();
if(this.customSelection){
plain = this.customSelection;
if(this.table.options.clipboardCopyFormatter){
plain = this.table.options.clipboardCopyFormatter("plain", plain);
}
}else {
list = this.table.modules.export.generateExportList(this.table.options.clipboardCopyConfig, this.table.options.clipboardCopyStyled, this.rowRange, "clipboard");
html = this.table.modules.export.generateHTMLTable(list);
plain = html ? this.generatePlainContent(list) : "";
if(this.table.options.clipboardCopyFormatter){
plain = this.table.options.clipboardCopyFormatter("plain", plain);
html = this.table.options.clipboardCopyFormatter("html", html);
}
}
if (window.clipboardData && window.clipboardData.setData) {
window.clipboardData.setData('Text', plain);
} else if (e.clipboardData && e.clipboardData.setData) {
e.clipboardData.setData('text/plain', plain);
if(html){
e.clipboardData.setData('text/html', html);
}
} else if (e.originalEvent && e.originalEvent.clipboardData.setData) {
e.originalEvent.clipboardData.setData('text/plain', plain);
if(html){
e.originalEvent.clipboardData.setData('text/html', html);
}
}
this.dispatchExternal("clipboardCopied", plain, html);
this.reset();
}
});
}
if(this.mode === true || this.mode === "paste"){
this.table.element.addEventListener("paste", (e) => {
this.paste(e);
});
}
this.setPasteParser(this.table.options.clipboardPasteParser);
this.setPasteAction(this.table.options.clipboardPasteAction);
this.registerTableFunction("copyToClipboard", this.copy.bind(this));
}
reset(){
this.blocked = true;
this.customSelection = false;
}
generatePlainContent (list) {
var output = [];
list.forEach((row) => {
var rowData = [];
row.columns.forEach((col) => {
var value = "";
if(col){
if(row.type === "group"){
col.value = col.component.getKey();
}
if(col.value === null){
value = "";
}else {
switch(typeof col.value){
case "object":
value = JSON.stringify(col.value);
break;
case "undefined":
value = "";
break;
default:
value = col.value;
}
}
}
rowData.push(value);
});
output.push(rowData.join("\t"));
});
return output.join("\n");
}
copy (range, internal) {
var sel, textRange;
this.blocked = false;
this.customSelection = false;
if (this.mode === true || this.mode === "copy") {
this.rowRange = range || this.table.options.clipboardCopyRowRange;
if (typeof window.getSelection != "undefined" && typeof document.createRange != "undefined") {
range = document.createRange();
range.selectNodeContents(this.table.element);
sel = window.getSelection();
if (sel.toString() && internal) {
this.customSelection = sel.toString();
}
sel.removeAllRanges();
sel.addRange(range);
} else if (typeof document.selection != "undefined" && typeof document.body.createTextRange != "undefined") {
textRange = document.body.createTextRange();
textRange.moveToElementText(this.table.element);
textRange.select();
}
document.execCommand('copy');
if (sel) {
sel.removeAllRanges();
}
}
}
//PASTE EVENT HANDLING
setPasteAction(action){
switch(typeof action){
case "string":
this.pasteAction = Clipboard.pasteActions[action];
if(!this.pasteAction){
console.warn("Clipboard Error - No such paste action found:", action);
}
break;
case "function":
this.pasteAction = action;
break;
}
}
setPasteParser(parser){
switch(typeof parser){
case "string":
this.pasteParser = Clipboard.pasteParsers[parser];
if(!this.pasteParser){
console.warn("Clipboard Error - No such paste parser found:", parser);
}
break;
case "function":
this.pasteParser = parser;
break;
}
}
paste(e){
var data, rowData, rows;
if(this.checkPaseOrigin(e)){
data = this.getPasteData(e);
rowData = this.pasteParser.call(this, data);
if(rowData){
e.preventDefault();
if(this.table.modExists("mutator")){
rowData = this.mutateData(rowData);
}
rows = this.pasteAction.call(this, rowData);
this.dispatchExternal("clipboardPasted", data, rowData, rows);
}else {
this.dispatchExternal("clipboardPasteError", data);
}
}
}
mutateData(data){
var output = [];
if(Array.isArray(data)){
data.forEach((row) => {
output.push(this.table.modules.mutator.transformRow(row, "clipboard"));
});
}else {
output = data;
}
return output;
}
checkPaseOrigin(e){
var valid = true;
if(e.target.tagName != "DIV" || this.table.modules.edit.currentCell){
valid = false;
}
return valid;
}
getPasteData(e){
var data;
if (window.clipboardData && window.clipboardData.getData) {
data = window.clipboardData.getData('Text');
} else if (e.clipboardData && e.clipboardData.getData) {
data = e.clipboardData.getData('text/plain');
} else if (e.originalEvent && e.originalEvent.clipboardData.getData) {
data = e.originalEvent.clipboardData.getData('text/plain');
}
return data;
}
}
Clipboard.moduleName = "clipboard";
//load defaults
Clipboard.pasteActions = defaultPasteActions;
Clipboard.pasteParsers = defaultPasteParsers;
class CalcComponent{
constructor (row){
this._row = row;
return new Proxy(this, {
get: function(target, name, receiver) {
if (typeof target[name] !== "undefined") {
return target[name];
}else {
return target._row.table.componentFunctionBinder.handle("row", target._row, name);
}
}
});
}
getData(transform){
return this._row.getData(transform);
}
getElement(){
return this._row.getElement();
}
getTable(){
return this._row.table;
}
getCells(){
var cells = [];
this._row.getCells().forEach(function(cell){
cells.push(cell.getComponent());
});
return cells;
}
getCell(column){
var cell = this._row.getCell(column);
return cell ? cell.getComponent() : false;
}
_getSelf(){
return this._row;
}
}
//public cell object
class CellComponent {
constructor (cell){
this._cell = cell;
return new Proxy(this, {
get: function(target, name, receiver) {
if (typeof target[name] !== "undefined") {
return target[name];
}else {
return target._cell.table.componentFunctionBinder.handle("cell", target._cell, name);
}
}
});
}
getValue(){
return this._cell.getValue();
}
getOldValue(){
return this._cell.getOldValue();
}
getInitialValue(){
return this._cell.initialValue;
}
getElement(){
return this._cell.getElement();
}
getRow(){
return this._cell.row.getComponent();
}
getData(){
return this._cell.row.getData();
}
getField(){
return this._cell.column.getField();
}
getColumn(){
return this._cell.column.getComponent();
}
setValue(value, mutate){
if(typeof mutate == "undefined"){
mutate = true;
}
this._cell.setValue(value, mutate);
}
restoreOldValue(){
this._cell.setValueActual(this._cell.getOldValue());
}
restoreInitialValue(){
this._cell.setValueActual(this._cell.initialValue);
}
checkHeight(){
this._cell.checkHeight();
}
getTable(){
return this._cell.table;
}
_getSelf(){
return this._cell;
}
}
class Cell extends CoreFeature{
constructor(column, row){
super(column.table);
this.table = column.table;
this.column = column;
this.row = row;
this.element = null;
this.value = null;
this.initialValue;
this.oldValue = null;
this.modules = {};
this.height = null;
this.width = null;
this.minWidth = null;
this.component = null;
this.loaded = false; //track if the cell has been added to the DOM yet
this.build();
}
//////////////// Setup Functions /////////////////
//generate element
build(){
this.generateElement();
this.setWidth();
this._configureCell();
this.setValueActual(this.column.getFieldValue(this.row.data));
this.initialValue = this.value;
}
generateElement(){
this.element = document.createElement('div');
this.element.className = "tabulator-cell";
this.element.setAttribute("role", "gridcell");
}
_configureCell(){
var element = this.element,
field = this.column.getField(),
vertAligns = {
top:"flex-start",
bottom:"flex-end",
middle:"center",
},
hozAligns = {
left:"flex-start",
right:"flex-end",
center:"center",
};
//set text alignment
element.style.textAlign = this.column.hozAlign;
if(this.column.vertAlign){
element.style.display = "inline-flex";
element.style.alignItems = vertAligns[this.column.vertAlign] || "";
if(this.column.hozAlign){
element.style.justifyContent = hozAligns[this.column.hozAlign] || "";
}
}
if(field){
element.setAttribute("tabulator-field", field);
}
//add class to cell if needed
if(this.column.definition.cssClass){
var classNames = this.column.definition.cssClass.split(" ");
classNames.forEach((className) => {
element.classList.add(className);
});
}
this.dispatch("cell-init", this);
//hide cell if not visible
if(!this.column.visible){
this.hide();
}
}
//generate cell contents
_generateContents(){
var val;
val = this.chain("cell-format", this, null, () => {
return this.element.innerHTML = this.value;
});
switch(typeof val){
case "object":
if(val instanceof Node){
//clear previous cell contents
while(this.element.firstChild) this.element.removeChild(this.element.firstChild);
this.element.appendChild(val);
}else {
this.element.innerHTML = "";
if(val != null){
console.warn("Format Error - Formatter has returned a type of object, the only valid formatter object return is an instance of Node, the formatter returned:", val);
}
}
break;
case "undefined":
this.element.innerHTML = "";
break;
default:
this.element.innerHTML = val;
}
}
cellRendered(){
this.dispatch("cell-rendered", this);
}
//////////////////// Getters ////////////////////
getElement(containerOnly){
if(!this.loaded){
this.loaded = true;
if(!containerOnly){
this.layoutElement();
}
}
return this.element;
}
getValue(){
return this.value;
}
getOldValue(){
return this.oldValue;
}
//////////////////// Actions ////////////////////
setValue(value, mutate, force){
var changed = this.setValueProcessData(value, mutate, force);
if(changed){
this.dispatch("cell-value-updated", this);
this.cellRendered();
if(this.column.definition.cellEdited){
this.column.definition.cellEdited.call(this.table, this.getComponent());
}
this.dispatchExternal("cellEdited", this.getComponent());
if(this.subscribedExternal("dataChanged")){
this.dispatchExternal("dataChanged", this.table.rowManager.getData());
}
}
}
setValueProcessData(value, mutate, force){
var changed = false;
if(this.value !== value || force){
changed = true;
if(mutate){
value = this.chain("cell-value-changing", [this, value], null, value);
}
}
this.setValueActual(value);
if(changed){
this.dispatch("cell-value-changed", this);
}
return changed;
}
setValueActual(value){
this.oldValue = this.value;
this.value = value;
this.dispatch("cell-value-save-before", this);
this.column.setFieldValue(this.row.data, value);
this.dispatch("cell-value-save-after", this);
if(this.loaded){
this.layoutElement();
}
}
layoutElement(){
this._generateContents();
this.dispatch("cell-layout", this);
}
setWidth(){
this.width = this.column.width;
this.element.style.width = this.column.widthStyled;
}
clearWidth(){
this.width = "";
this.element.style.width = "";
}
getWidth(){
return this.width || this.element.offsetWidth;
}
setMinWidth(){
this.minWidth = this.column.minWidth;
this.element.style.minWidth = this.column.minWidthStyled;
}
setMaxWidth(){
this.maxWidth = this.column.maxWidth;
this.element.style.maxWidth = this.column.maxWidthStyled;
}
checkHeight(){
// var height = this.element.css("height");
this.row.reinitializeHeight();
}
clearHeight(){
this.element.style.height = "";
this.height = null;
this.dispatch("cell-height", this, "");
}
setHeight(){
this.height = this.row.height;
this.element.style.height = this.row.heightStyled;
this.dispatch("cell-height", this, this.row.heightStyled);
}
getHeight(){
return this.height || this.element.offsetHeight;
}
show(){
this.element.style.display = this.column.vertAlign ? "inline-flex" : "";
}
hide(){
this.element.style.display = "none";
}
delete(){
this.dispatch("cell-delete", this);
if(!this.table.rowManager.redrawBlock && this.element.parentNode){
this.element.parentNode.removeChild(this.element);
}
this.element = false;
this.column.deleteCell(this);
this.row.deleteCell(this);
this.calcs = {};
}
getIndex(){
return this.row.getCellIndex(this);
}
//////////////// Object Generation /////////////////
getComponent(){
if(!this.component){
this.component = new CellComponent(this);
}
return this.component;
}
}
//public column object
class ColumnComponent {
constructor (column){
this._column = column;
this.type = "ColumnComponent";
return new Proxy(this, {
get: function(target, name, receiver) {
if (typeof target[name] !== "undefined") {
return target[name];
}else {
return target._column.table.componentFunctionBinder.handle("column", target._column, name);
}
}
});
}
getElement(){
return this._column.getElement();
}
getDefinition(){
return this._column.getDefinition();
}
getField(){
return this._column.getField();
}
getTitleDownload() {
return this._column.getTitleDownload();
}
getCells(){
var cells = [];
this._column.cells.forEach(function(cell){
cells.push(cell.getComponent());
});
return cells;
}
isVisible(){
return this._column.visible;
}
show(){
if(this._column.isGroup){
this._column.columns.forEach(function(column){
column.show();
});
}else {
this._column.show();
}
}
hide(){
if(this._column.isGroup){
this._column.columns.forEach(function(column){
column.hide();
});
}else {
this._column.hide();
}
}
toggle(){
if(this._column.visible){
this.hide();
}else {
this.show();
}
}
delete(){
return this._column.delete();
}
getSubColumns(){
var output = [];
if(this._column.columns.length){
this._column.columns.forEach(function(column){
output.push(column.getComponent());
});
}
return output;
}
getParentColumn(){
return this._column.parent instanceof Column ? this._column.parent.getComponent() : false;
}
_getSelf(){
return this._column;
}
scrollTo(){
return this._column.table.columnManager.scrollToColumn(this._column);
}
getTable(){
return this._column.table;
}
move(to, after){
var toColumn = this._column.table.columnManager.findColumn(to);
if(toColumn){
this._column.table.columnManager.moveColumn(this._column, toColumn, after);
}else {
console.warn("Move Error - No matching column found:", toColumn);
}
}
getNextColumn(){
var nextCol = this._column.nextColumn();
return nextCol ? nextCol.getComponent() : false;
}
getPrevColumn(){
var prevCol = this._column.prevColumn();
return prevCol ? prevCol.getComponent() : false;
}
updateDefinition(updates){
return this._column.updateDefinition(updates);
}
getWidth(){
return this._column.getWidth();
}
setWidth(width){
var result;
if(width === true){
result = this._column.reinitializeWidth(true);
}else {
result = this._column.setWidth(width);
}
this._column.table.columnManager.rerenderColumns(true);
return result;
}
}
var defaultColumnOptions = {
"title": undefined,
"field": undefined,
"columns": undefined,
"visible": undefined,
"hozAlign": undefined,
"vertAlign": undefined,
"width": undefined,
"minWidth": 40,
"maxWidth": undefined,
"maxInitialWidth": undefined,
"cssClass": undefined,
"variableHeight": undefined,
"headerVertical": undefined,
"headerHozAlign": undefined,
"headerWordWrap": false,
"editableTitle": undefined,
};
class Column extends CoreFeature{
constructor(def, parent){
super(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 = this.createElement(); //column header element
this.contentElement = false;
this.titleHolderElement = false;
this.titleElement = false;
this.groupElement = this.createGroupElement(); //column group holder element
this.isGroup = false;
this.hozAlign = ""; //horizontal text alignment
this.vertAlign = ""; //vert text alignment
//multi dimensional filed handling
this.field ="";
this.fieldStructure = "";
this.getFieldValue = "";
this.setFieldValue = "";
this.titleDownload = null;
this.titleFormatterRendered = false;
this.mapDefinitions();
this.setField(this.definition.field);
this.modules = {}; //hold module variables;
this.width = null; //column width
this.widthStyled = ""; //column width pre-styled to improve render efficiency
this.maxWidth = null; //column maximum width
this.maxWidthStyled = ""; //column maximum pre-styled to improve render efficiency
this.maxInitialWidth = null;
this.minWidth = null; //column minimum width
this.minWidthStyled = ""; //column minimum pre-styled to improve render efficiency
this.widthFixed = false; //user has specified a width for this column
this.visible = true; //default visible state
this.component = null;
//initialize column
if(this.definition.columns){
this.isGroup = true;
this.definition.columns.forEach((def, i) => {
var newCol = new Column(def, this);
this.attachColumn(newCol);
});
this.checkColumnVisibility();
}else {
parent.registerColumnField(this);
}
this._initialize();
}
createElement (){
var el = document.createElement("div");
el.classList.add("tabulator-col");
el.setAttribute("role", "columnheader");
el.setAttribute("aria-sort", "none");
switch(this.table.options.columnHeaderVertAlign){
case "middle":
el.style.justifyContent = "center";
break;
case "bottom":
el.style.justifyContent = "flex-end";
break;
}
return el;
}
createGroupElement (){
var el = document.createElement("div");
el.classList.add("tabulator-col-group-cols");
return el;
}
mapDefinitions(){
var defaults = this.table.options.columnDefaults;
//map columnDefaults onto column definitions
if(defaults){
for(let key in defaults){
if(typeof this.definition[key] === "undefined"){
this.definition[key] = defaults[key];
}
}
}
this.definition = this.table.columnManager.optionsList.generate(Column.defaultOptionList, this.definition);
}
checkDefinition(){
Object.keys(this.definition).forEach((key) => {
if(Column.defaultOptionList.indexOf(key) === -1){
console.warn("Invalid column definition option in '" + (this.field || this.definition.title) + "' column:", key);
}
});
}
setField(field){
this.field = field;
this.fieldStructure = field ? (this.table.options.nestedFieldSeparator ? field.split(this.table.options.nestedFieldSeparator) : [field]) : [];
this.getFieldValue = this.fieldStructure.length > 1 ? this._getNestedData : this._getFlatData;
this.setFieldValue = this.fieldStructure.length > 1 ? this._setNestedData : this._setFlatData;
}
//register column position with column manager
registerColumnPosition(column){
this.parent.registerColumnPosition(column);
}
//register column position with column manager
registerColumnField(column){
this.parent.registerColumnField(column);
}
//trigger position registration
reRegisterPosition(){
if(this.isGroup){
this.columns.forEach(function(column){
column.reRegisterPosition();
});
}else {
this.registerColumnPosition(this);
}
}
//build header element
_initialize(){
var def = this.definition;
while(this.element.firstChild) this.element.removeChild(this.element.firstChild);
if(def.headerVertical){
this.element.classList.add("tabulator-col-vertical");
if(def.headerVertical === "flip"){
this.element.classList.add("tabulator-col-vertical-flip");
}
}
this.contentElement = this._buildColumnHeaderContent();
this.element.appendChild(this.contentElement);
if(this.isGroup){
this._buildGroupHeader();
}else {
this._buildColumnHeader();
}
this.dispatch("column-init", this);
}
//build header element for header
_buildColumnHeader(){
var def = this.definition;
this.dispatch("column-layout", this);
//set column visibility
if(typeof def.visible != "undefined"){
if(def.visible){
this.show(true);
}else {
this.hide(true);
}
}
//assign additional css classes to column header
if(def.cssClass){
var classNames = def.cssClass.split(" ");
classNames.forEach((className) => {
this.element.classList.add(className);
});
}
if(def.field){
this.element.setAttribute("tabulator-field", def.field);
}
//set min width if present
this.setMinWidth(parseInt(def.minWidth));
if (def.maxInitialWidth) {
this.maxInitialWidth = parseInt(def.maxInitialWidth);
}
if(def.maxWidth){
this.setMaxWidth(parseInt(def.maxWidth));
}
this.reinitializeWidth();
//set horizontal text alignment
this.hozAlign = this.definition.hozAlign;
this.vertAlign = this.definition.vertAlign;
this.titleElement.style.textAlign = this.definition.headerHozAlign;
}
_buildColumnHeaderContent(){
var contentElement = document.createElement("div");
contentElement.classList.add("tabulator-col-content");
this.titleHolderElement = document.createElement("div");
this.titleHolderElement.classList.add("tabulator-col-title-holder");
contentElement.appendChild(this.titleHolderElement);
this.titleElement = this._buildColumnHeaderTitle();
this.titleHolderElement.appendChild(this.titleElement);
return contentElement;
}
//build title element of column
_buildColumnHeaderTitle(){
var def = this.definition;
var titleHolderElement = document.createElement("div");
titleHolderElement.classList.add("tabulator-col-title");
if(def.headerWordWrap){
titleHolderElement.classList.add("tabulator-col-title-wrap");
}
if(def.editableTitle){
var titleElement = document.createElement("input");
titleElement.classList.add("tabulator-title-editor");
titleElement.addEventListener("click", (e) => {
e.stopPropagation();
titleElement.focus();
});
titleElement.addEventListener("change", () => {
def.title = titleElement.value;
this.dispatchExternal("columnTitleChanged", this.getComponent());
});
titleHolderElement.appendChild(titleElement);
if(def.field){
this.langBind("columns|" + def.field, (text) => {
titleElement.value = text || (def.title || " ");
});
}else {
titleElement.value = def.title || " ";
}
}else {
if(def.field){
this.langBind("columns|" + def.field, (text) => {
this._formatColumnHeaderTitle(titleHolderElement, text || (def.title || " "));
});
}else {
this._formatColumnHeaderTitle(titleHolderElement, def.title || " ");
}
}
return titleHolderElement;
}
_formatColumnHeaderTitle(el, title){
var contents = this.chain("column-format", [this, title, el], null, () => {
return title;
});
switch(typeof contents){
case "object":
if(contents instanceof Node){
el.appendChild(contents);
}else {
el.innerHTML = "";
console.warn("Format Error - Title formatter has returned a type of object, the only valid formatter object return is an instance of Node, the formatter returned:", contents);
}
break;
case "undefined":
el.innerHTML = "";
break;
default:
el.innerHTML = contents;
}
}
//build header element for column group
_buildGroupHeader(){
this.element.classList.add("tabulator-col-group");
this.element.setAttribute("role", "columngroup");
this.element.setAttribute("aria-title", this.definition.title);
//asign additional css classes to column header
if(this.definition.cssClass){
var classNames = this.definition.cssClass.split(" ");
classNames.forEach((className) => {
this.element.classList.add(className);
});
}
this.titleElement.style.textAlign = this.definition.headerHozAlign;
this.element.appendChild(this.groupElement);
}
//flat field lookup
_getFlatData(data){
return data[this.field];
}
//nested field lookup
_getNestedData(data){
var dataObj = data,
structure = this.fieldStructure,
length = structure.length,
output;
for(let i = 0; i < length; i++){
dataObj = dataObj[structure[i]];
output = dataObj;
if(!dataObj){
break;
}
}
return output;
}
//flat field set
_setFlatData(data, value){
if(this.field){
data[this.field] = value;
}
}
//nested field set
_setNestedData(data, value){
var dataObj = data,
structure = this.fieldStructure,
length = structure.length;
for(let i = 0; i < length; i++){
if(i == length -1){
dataObj[structure[i]] = value;
}else {
if(!dataObj[structure[i]]){
if(typeof value !== "undefined"){
dataObj[structure[i]] = {};
}else {
break;
}
}
dataObj = dataObj[structure[i]];
}
}
}
//attach column to this group
attachColumn(column){
if(this.groupElement){
this.columns.push(column);
this.groupElement.appendChild(column.getElement());
column.columnRendered();
}else {
console.warn("Column Warning - Column being attached to another column instead of column group");
}
}
//vertically align header in column
verticalAlign(alignment, height){
//calculate height of column header and group holder element
var parentHeight = this.parent.isGroup ? this.parent.getGroupElement().clientHeight : (height || this.parent.getHeadersElement().clientHeight);
// var parentHeight = this.parent.isGroup ? this.parent.getGroupElement().clientHeight : this.parent.getHeadersElement().clientHeight;
this.element.style.height = parentHeight + "px";
this.dispatch("column-height", this, this.element.style.height);
if(this.isGroup){
this.groupElement.style.minHeight = (parentHeight - this.contentElement.offsetHeight) + "px";
}
//vertically align cell contents
// if(!this.isGroup && alignment !== "top"){
// if(alignment === "bottom"){
// this.element.style.paddingTop = (this.element.clientHeight - this.contentElement.offsetHeight) + "px";
// }else{
// this.element.style.paddingTop = ((this.element.clientHeight - this.contentElement.offsetHeight) / 2) + "px";
// }
// }
this.columns.forEach(function(column){
column.verticalAlign(alignment);
});
}
//clear vertical alignment
clearVerticalAlign(){
this.element.style.paddingTop = "";
this.element.style.height = "";
this.element.style.minHeight = "";
this.groupElement.style.minHeight = "";
this.columns.forEach(function(column){
column.clearVerticalAlign();
});
this.dispatch("column-height", this, "");
}
//// Retrieve Column Information ////
//return column header element
getElement(){
return this.element;
}
//return column group element
getGroupElement(){
return this.groupElement;
}
//return field name
getField(){
return this.field;
}
getTitleDownload() {
return this.titleDownload;
}
//return the first column in a group
getFirstColumn(){
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
getLastColumn(){
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
getColumns(traverse){
var columns = [];
if(traverse){
this.columns.forEach((column) => {
columns.push(column);
columns = columns.concat(column.getColumns(true));
});
}else {
columns = this.columns;
}
return columns;
}
//return all columns in a group
getCells(){
return this.cells;
}
//retrieve the top column in a group of columns
getTopColumn(){
if(this.parent.isGroup){
return this.parent.getTopColumn();
}else {
return this;
}
}
//return column definition object
getDefinition(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 ////////////////////
checkColumnVisibility(){
var visible = false;
this.columns.forEach(function(column){
if(column.visible){
visible = true;
}
});
if(visible){
this.show();
this.dispatchExternal("columnVisibilityChanged", this.getComponent(), false);
}else {
this.hide();
}
}
//show column
show(silent, responsiveToggle){
if(!this.visible){
this.visible = true;
this.element.style.display = "";
if(this.parent.isGroup){
this.parent.checkColumnVisibility();
}
this.cells.forEach(function(cell){
cell.show();
});
if(!this.isGroup && this.width === null){
this.reinitializeWidth();
}
this.table.columnManager.verticalAlignHeaders();
this.dispatch("column-show", this, responsiveToggle);
if(!silent){
this.dispatchExternal("columnVisibilityChanged", this.getComponent(), true);
}
if(this.parent.isGroup){
this.parent.matchChildWidths();
}
if(!this.silent){
this.table.columnManager.rerenderColumns();
}
}
}
//hide column
hide(silent, responsiveToggle){
if(this.visible){
this.visible = false;
this.element.style.display = "none";
this.table.columnManager.verticalAlignHeaders();
if(this.parent.isGroup){
this.parent.checkColumnVisibility();
}
this.cells.forEach(function(cell){
cell.hide();
});
this.dispatch("column-hide", this, responsiveToggle);
if(!silent){
this.dispatchExternal("columnVisibilityChanged", this.getComponent(), false);
}
if(this.parent.isGroup){
this.parent.matchChildWidths();
}
if(!this.silent){
this.table.columnManager.rerenderColumns();
}
}
}
matchChildWidths(){
var childWidth = 0;
if(this.contentElement && this.columns.length){
this.columns.forEach(function(column){
if(column.visible){
childWidth += column.getWidth();
}
});
this.contentElement.style.maxWidth = (childWidth - 1) + "px";
if(this.parent.isGroup){
this.parent.matchChildWidths();
}
}
}
removeChild(child){
var index = this.columns.indexOf(child);
if(index > -1){
this.columns.splice(index, 1);
}
if(!this.columns.length){
this.delete();
}
}
setWidth(width){
this.widthFixed = true;
this.setWidthActual(width);
}
setWidthActual(width){
if(isNaN(width)){
width = Math.floor((this.table.element.clientWidth/100) * parseInt(width));
}
width = Math.max(this.minWidth, width);
if(this.maxWidth){
width = Math.min(this.maxWidth, width);
}
this.width = width;
this.widthStyled = width ? width + "px" : "";
this.element.style.width = this.widthStyled;
if(!this.isGroup){
this.cells.forEach(function(cell){
cell.setWidth();
});
}
if(this.parent.isGroup){
this.parent.matchChildWidths();
}
this.dispatch("column-width", this);
}
checkCellHeights(){
var rows = [];
this.cells.forEach(function(cell){
if(cell.row.heightInitialized){
if(cell.row.getElement().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();
});
}
getWidth(){
var width = 0;
if(this.isGroup){
this.columns.forEach(function(column){
if(column.visible){
width += column.getWidth();
}
});
}else {
width = this.width;
}
return width;
}
getLeftOffset(){
var offset = this.element.offsetLeft;
if(this.parent.isGroup){
offset += this.parent.getLeftOffset();
}
return offset;
}
getHeight(){
return Math.ceil(this.element.getBoundingClientRect().height);
}
setMinWidth(minWidth){
if(this.maxWidth && minWidth > this.maxWidth){
minWidth = this.maxWidth;
console.warn("the minWidth ("+ minWidth + "px) for column '" + this.field + "' cannot be bigger that its maxWidth ("+ this.maxWidthStyled + ")");
}
this.minWidth = minWidth;
this.minWidthStyled = minWidth ? minWidth + "px" : "";
this.element.style.minWidth = this.minWidthStyled;
this.cells.forEach(function(cell){
cell.setMinWidth();
});
}
setMaxWidth(maxWidth){
if(this.minWidth && maxWidth < this.minWidth){
maxWidth = this.minWidth;
console.warn("the maxWidth ("+ maxWidth + "px) for column '" + this.field + "' cannot be smaller that its minWidth ("+ this.minWidthStyled + ")");
}
this.maxWidth = maxWidth;
this.maxWidthStyled = maxWidth ? maxWidth + "px" : "";
this.element.style.maxWidth = this.maxWidthStyled;
this.cells.forEach(function(cell){
cell.setMaxWidth();
});
}
delete(){
return new Promise((resolve, reject) => {
if(this.isGroup){
this.columns.forEach(function(column){
column.delete();
});
}
this.dispatch("column-delete", this);
var cellCount = this.cells.length;
for(let i = 0; i < cellCount; i++){
this.cells[0].delete();
}
if(this.element.parentNode){
this.element.parentNode.removeChild(this.element);
}
this.element = false;
this.contentElement = false;
this.titleElement = false;
this.groupElement = false;
if(this.parent.isGroup){
this.parent.removeChild(this);
}
this.table.columnManager.deregisterColumn(this);
this.table.columnManager.rerenderColumns(true);
resolve();
});
}
columnRendered(){
if(this.titleFormatterRendered){
this.titleFormatterRendered();
}
this.dispatch("column-rendered", this);
}
//////////////// Cell Management /////////////////
//generate cell for this column
generateCell(row){
var cell = new Cell(this, row);
this.cells.push(cell);
return cell;
}
nextColumn(){
var index = this.table.columnManager.findColumnIndex(this);
return index > -1 ? this._nextVisibleColumn(index + 1) : false;
}
_nextVisibleColumn(index){
var column = this.table.columnManager.getColumnByIndex(index);
return !column || column.visible ? column : this._nextVisibleColumn(index + 1);
}
prevColumn(){
var index = this.table.columnManager.findColumnIndex(this);
return index > -1 ? this._prevVisibleColumn(index - 1) : false;
}
_prevVisibleColumn(index){
var column = this.table.columnManager.getColumnByIndex(index);
return !column || column.visible ? column : this._prevVisibleColumn(index - 1);
}
reinitializeWidth(force){
this.widthFixed = false;
//set width if present
if(typeof this.definition.width !== "undefined" && !force){
// maxInitialWidth ignored here as width specified
this.setWidth(this.definition.width);
}
this.dispatch("column-width-fit-before", this);
this.fitToData(force);
this.dispatch("column-width-fit-after", this);
}
//set column width to maximum cell width for non group columns
fitToData(force){
if(this.isGroup){
return;
}
if(!this.widthFixed){
this.element.style.width = "";
this.cells.forEach((cell) => {
cell.clearWidth();
});
}
var maxWidth = this.element.offsetWidth;
if(!this.width || !this.widthFixed){
this.cells.forEach((cell) => {
var width = cell.getWidth();
if(width > maxWidth){
maxWidth = width;
}
});
if(maxWidth){
var setTo = maxWidth + 1;
if (this.maxInitialWidth && !force) {
setTo = Math.min(setTo, this.maxInitialWidth);
}
this.setWidthActual(setTo);
}
}
}
updateDefinition(updates){
var definition;
if(!this.isGroup){
if(!this.parent.isGroup){
definition = Object.assign({}, this.getDefinition());
definition = Object.assign(definition, updates);
return this.table.columnManager.addColumn(definition, false, this)
.then((column) => {
if(definition.field == this.field){
this.field = false; //clear field name to prevent deletion of duplicate column from arrays
}
return this.delete()
.then(() => {
return column.getComponent();
});
});
}else {
console.error("Column Update Error - The updateDefinition function is only available on ungrouped columns");
return Promise.reject("Column Update Error - The updateDefinition function is only available on columns, not column groups");
}
}else {
console.error("Column Update Error - The updateDefinition function is only available on ungrouped columns");
return Promise.reject("Column Update Error - The updateDefinition function is only available on columns, not column groups");
}
}
deleteCell(cell){
var index = this.cells.indexOf(cell);
if(index > -1){
this.cells.splice(index, 1);
}
}
//////////////// Object Generation /////////////////
getComponent(){
if(!this.component){
this.component = new ColumnComponent(this);
}
return this.component;
}
}
Column.defaultOptionList = defaultColumnOptions;
//public row object
class RowComponent {
constructor (row){
this._row = row;
return new Proxy(this, {
get: function(target, name, receiver) {
if (typeof target[name] !== "undefined") {
return target[name];
}else {
return target._row.table.componentFunctionBinder.handle("row", target._row, name);
}
}
});
}
getData(transform){
return this._row.getData(transform);
}
getElement(){
return this._row.getElement();
}
getCells(){
var cells = [];
this._row.getCells().forEach(function(cell){
cells.push(cell.getComponent());
});
return cells;
}
getCell(column){
var cell = this._row.getCell(column);
return cell ? cell.getComponent() : false;
}
getIndex(){
return this._row.getData("data")[this._row.table.options.index];
}
getPosition(){
return this._row.getPosition();
}
watchPosition(callback){
return this._row.watchPosition(callback);
}
delete(){
return this._row.delete();
}
scrollTo(){
return this._row.table.rowManager.scrollToRow(this._row);
}
move(to, after){
this._row.moveToRow(to, after);
}
update(data){
return this._row.updateData(data);
}
normalizeHeight(){
this._row.normalizeHeight(true);
}
_getSelf(){
return this._row;
}
reformat(){
return this._row.reinitialize();
}
getTable(){
return this._row.table;
}
getNextRow(){
var row = this._row.nextRow();
return row ? row.getComponent() : row;
}
getPrevRow(){
var row = this._row.prevRow();
return row ? row.getComponent() : row;
}
}
class Row extends CoreFeature{
constructor (data, parent, type = "row"){
super(parent.table);
this.parent = parent;
this.data = {};
this.type = type; //type of element
this.element = false;
this.modules = {}; //hold module variables;
this.cells = [];
this.height = 0; //hold element height
this.heightStyled = ""; //hold element height pre-styled to improve render efficiency
this.manualHeight = false; //user has manually set row height
this.outerHeight = 0; //hold elements outer height
this.initialized = false; //element has been rendered
this.heightInitialized = false; //element has resized cells to fit
this.position = 0; //store position of element in row list
this.positionWatchers = [];
this.component = null;
this.created = false;
this.setData(data);
}
create(){
if(!this.created){
this.created = true;
this.generateElement();
}
}
createElement (){
var el = document.createElement("div");
el.classList.add("tabulator-row");
el.setAttribute("role", "row");
this.element = el;
}
getElement(){
this.create();
return this.element;
}
detachElement(){
if (this.element && this.element.parentNode){
this.element.parentNode.removeChild(this.element);
}
}
generateElement(){
this.createElement();
this.dispatch("row-init", this);
}
generateCells(){
this.cells = this.table.columnManager.generateCells(this);
}
//functions to setup on first render
initialize(force){
this.create();
if(!this.initialized || force){
this.deleteCells();
while(this.element.firstChild) this.element.removeChild(this.element.firstChild);
this.dispatch("row-layout-before", this);
this.generateCells();
this.initialized = true;
this.table.columnManager.renderer.renderRowCells(this);
if(force){
this.normalizeHeight();
}
this.dispatch("row-layout", this);
if(this.table.options.rowFormatter){
this.table.options.rowFormatter(this.getComponent());
}
this.dispatch("row-layout-after", this);
}else {
this.table.columnManager.renderer.rerenderRowCells(this);
}
}
reinitializeHeight(){
this.heightInitialized = false;
if(this.element && this.element.offsetParent !== null){
this.normalizeHeight(true);
}
}
deinitialize(){
this.initialized = false;
}
deinitializeHeight(){
this.heightInitialized = false;
}
reinitialize(children){
this.initialized = false;
this.heightInitialized = false;
if(!this.manualHeight){
this.height = 0;
this.heightStyled = "";
}
if(this.element && this.element.offsetParent !== null){
this.initialize(true);
}
this.dispatch("row-relayout", this);
}
//get heights when doing bulk row style calcs in virtual DOM
calcHeight(force){
var maxHeight = 0,
minHeight;
if(this.table.options.rowHeight){
this.height = this.table.options.rowHeight;
}else {
minHeight = this.table.options.resizableRows ? this.element.clientHeight : 0;
this.cells.forEach(function(cell){
var height = cell.getHeight();
if(height > maxHeight){
maxHeight = height;
}
});
if(force){
this.height = Math.max(maxHeight, minHeight);
}else {
this.height = this.manualHeight ? this.height : Math.max(maxHeight, minHeight);
}
}
this.heightStyled = this.height ? this.height + "px" : "";
this.outerHeight = this.element.offsetHeight;
}
//set of cells
setCellHeight(){
this.cells.forEach(function(cell){
cell.setHeight();
});
this.heightInitialized = true;
}
clearCellHeight(){
this.cells.forEach(function(cell){
cell.clearHeight();
});
}
//normalize the height of elements in the row
normalizeHeight(force){
if(force && !this.table.options.rowHeight){
this.clearCellHeight();
}
this.calcHeight(force);
this.setCellHeight();
}
//set height of rows
setHeight(height, force){
if(this.height != height || force){
this.manualHeight = true;
this.height = height;
this.heightStyled = height ? height + "px" : "";
this.setCellHeight();
// this.outerHeight = this.element.outerHeight();
this.outerHeight = this.element.offsetHeight;
}
}
//return rows outer height
getHeight(){
return this.outerHeight;
}
//return rows outer Width
getWidth(){
return this.element.offsetWidth;
}
//////////////// Cell Management /////////////////
deleteCell(cell){
var index = this.cells.indexOf(cell);
if(index > -1){
this.cells.splice(index, 1);
}
}
//////////////// Data Management /////////////////
setData(data){
this.data = this.chain("row-data-init-before", [this, data], undefined, data);
this.dispatch("row-data-init-after", this);
}
//update the rows data
updateData(updatedData){
var visible = this.element && Helpers.elVisible(this.element),
tempData = {},
newRowData;
return new Promise((resolve, reject) => {
if(typeof updatedData === "string"){
updatedData = JSON.parse(updatedData);
}
this.dispatch("row-data-save-before", this);
if(this.subscribed("row-data-changing")){
tempData = Object.assign(tempData, this.data);
tempData = Object.assign(tempData, updatedData);
}
newRowData = this.chain("row-data-changing", [this, tempData, updatedData], null, updatedData);
//set data
for (let attrname in newRowData) {
this.data[attrname] = newRowData[attrname];
}
this.dispatch("row-data-save-after", this);
//update affected cells only
for (let attrname in updatedData) {
let columns = this.table.columnManager.getColumnsByFieldRoot(attrname);
columns.forEach((column) => {
let cell = this.getCell(column.getField());
if(cell){
let value = column.getFieldValue(newRowData);
if(cell.getValue() !== value){
cell.setValueProcessData(value);
if(visible){
cell.cellRendered();
}
}
}
});
}
//Partial reinitialization if visible
if(visible){
this.normalizeHeight(true);
if(this.table.options.rowFormatter){
this.table.options.rowFormatter(this.getComponent());
}
}else {
this.initialized = false;
this.height = 0;
this.heightStyled = "";
}
this.dispatch("row-data-changed", this, visible, updatedData);
//this.reinitialize();
this.dispatchExternal("rowUpdated", this.getComponent());
if(this.subscribedExternal("dataChanged")){
this.dispatchExternal("dataChanged", this.table.rowManager.getData());
}
resolve();
});
}
getData(transform){
if(transform){
return this.chain("row-data-retrieve", [this, transform], null, this.data);
}
return this.data;
}
getCell(column){
var match = false;
column = this.table.columnManager.findColumn(column);
if(!this.initialized && this.cells.length === 0){
this.generateCells();
}
match = this.cells.find(function(cell){
return cell.column === column;
});
return match;
}
getCellIndex(findCell){
return this.cells.findIndex(function(cell){
return cell === findCell;
});
}
findCell(subject){
return this.cells.find((cell) => {
return cell.element === subject;
});
}
getCells(){
if(!this.initialized && this.cells.length === 0){
this.generateCells();
}
return this.cells;
}
nextRow(){
var row = this.table.rowManager.nextDisplayRow(this, true);
return row || false;
}
prevRow(){
var row = this.table.rowManager.prevDisplayRow(this, true);
return row || false;
}
moveToRow(to, before){
var toRow = this.table.rowManager.findRow(to);
if(toRow){
this.table.rowManager.moveRowActual(this, toRow, !before);
this.table.rowManager.refreshActiveData("display", false, true);
}else {
console.warn("Move Error - No matching row found:", to);
}
}
///////////////////// Actions /////////////////////
delete(){
this.dispatch("row-delete", this);
this.deleteActual();
return Promise.resolve();
}
deleteActual(blockRedraw){
this.detachModules();
this.table.rowManager.deleteRow(this, blockRedraw);
this.deleteCells();
this.initialized = false;
this.heightInitialized = false;
this.element = false;
this.dispatch("row-deleted", this);
}
detachModules(){
this.dispatch("row-deleting", this);
}
deleteCells(){
var cellCount = this.cells.length;
for(let i = 0; i < cellCount; i++){
this.cells[0].delete();
}
}
wipe(){
this.detachModules();
this.deleteCells();
if(this.element){
while(this.element.firstChild) this.element.removeChild(this.element.firstChild);
if(this.element.parentNode){
this.element.parentNode.removeChild(this.element);
}
}
this.element = false;
this.modules = {};
}
isDisplayed(){
return this.table.rowManager.getDisplayRows().includes(this);
}
getPosition(){
return this.isDisplayed() ? this.position : false;
}
setPosition(position){
if(position != this.position){
this.position = position;
this.positionWatchers.forEach((callback) => {
callback(this.position);
});
}
}
watchPosition(callback){
this.positionWatchers.push(callback);
callback(this.position);
}
getGroup(){
return this.modules.group || false;
}
//////////////// Object Generation /////////////////
getComponent(){
if(!this.component){
this.component = new RowComponent(this);
}
return this.component;
}
}
var defaultCalculations = {
"avg":function(values, data, calcParams){
var output = 0,
precision = typeof calcParams.precision !== "undefined" ? calcParams.precision : 2;
if(values.length){
output = values.reduce(function(sum, value){
return Number(sum) + Number(value);
});
output = output / values.length;
output = precision !== false ? output.toFixed(precision) : output;
}
return parseFloat(output).toString();
},
"max":function(values, data, calcParams){
var output = null,
precision = typeof calcParams.precision !== "undefined" ? calcParams.precision : false;
values.forEach(function(value){
value = Number(value);
if(value > output || output === null){
output = value;
}
});
return output !== null ? (precision !== false ? output.toFixed(precision) : output) : "";
},
"min":function(values, data, calcParams){
var output = null,
precision = typeof calcParams.precision !== "undefined" ? calcParams.precision : false;
values.forEach(function(value){
value = Number(value);
if(value < output || output === null){
output = value;
}
});
return output !== null ? (precision !== false ? output.toFixed(precision) : output) : "";
},
"sum":function(values, data, calcParams){
var output = 0,
precision = typeof calcParams.precision !== "undefined" ? calcParams.precision : false;
if(values.length){
values.forEach(function(value){
value = Number(value);
output += !isNaN(value) ? Number(value) : 0;
});
}
return precision !== false ? output.toFixed(precision) : output;
},
"concat":function(values, data, calcParams){
var output = 0;
if(values.length){
output = values.reduce(function(sum, value){
return String(sum) + String(value);
});
}
return output;
},
"count":function(values, data, calcParams){
var output = 0;
if(values.length){
values.forEach(function(value){
if(value){
output ++;
}
});
}
return output;
},
};
class ColumnCalcs extends Module{
constructor(table){
super(table);
this.topCalcs = [];
this.botCalcs = [];
this.genColumn = false;
this.topElement = this.createElement();
this.botElement = this.createElement();
this.topRow = false;
this.botRow = false;
this.topInitialized = false;
this.botInitialized = false;
this.blocked = false;
this.recalcAfterBlock = false;
this.registerTableOption("columnCalcs", true);
this.registerColumnOption("topCalc");
this.registerColumnOption("topCalcParams");
this.registerColumnOption("topCalcFormatter");
this.registerColumnOption("topCalcFormatterParams");
this.registerColumnOption("bottomCalc");
this.registerColumnOption("bottomCalcParams");
this.registerColumnOption("bottomCalcFormatter");
this.registerColumnOption("bottomCalcFormatterParams");
}
createElement (){
var el = document.createElement("div");
el.classList.add("tabulator-calcs-holder");
return el;
}
initialize(){
this.genColumn = new Column({field:"value"}, this);
this.subscribe("cell-value-changed", this.cellValueChanged.bind(this));
this.subscribe("column-init", this.initializeColumnCheck.bind(this));
this.subscribe("row-deleted", this.rowsUpdated.bind(this));
this.subscribe("scroll-horizontal", this.scrollHorizontal.bind(this));
this.subscribe("row-added", this.rowsUpdated.bind(this));
this.subscribe("column-moved", this.recalcActiveRows.bind(this));
this.subscribe("column-add", this.recalcActiveRows.bind(this));
this.subscribe("data-refreshed", this.recalcActiveRowsRefresh.bind(this));
this.subscribe("table-redraw", this.tableRedraw.bind(this));
this.subscribe("rows-visible", this.visibleRows.bind(this));
this.subscribe("scrollbar-vertical", this.adjustForScrollbar.bind(this));
this.subscribe("redraw-blocked", this.blockRedraw.bind(this));
this.subscribe("redraw-restored", this.restoreRedraw.bind(this));
this.subscribe("table-redrawing", this.resizeHolderWidth.bind(this));
this.subscribe("column-resized", this.resizeHolderWidth.bind(this));
this.subscribe("column-show", this.resizeHolderWidth.bind(this));
this.subscribe("column-hide", this.resizeHolderWidth.bind(this));
this.registerTableFunction("getCalcResults", this.getResults.bind(this));
this.registerTableFunction("recalc", this.userRecalc.bind(this));
this.resizeHolderWidth();
}
resizeHolderWidth(){
this.topElement.style.minWidth = this.table.columnManager.headersElement.offsetWidth + "px";
}
tableRedraw(force){
this.recalc(this.table.rowManager.activeRows);
if(force){
this.redraw();
}
}
blockRedraw(){
this.blocked = true;
this.recalcAfterBlock = false;
}
restoreRedraw(){
this.blocked = false;
if(this.recalcAfterBlock){
this.recalcAfterBlock = false;
this.recalcActiveRowsRefresh();
}
}
///////////////////////////////////
///////// Table Functions /////////
///////////////////////////////////
userRecalc(){
this.recalc(this.table.rowManager.activeRows);
}
///////////////////////////////////
///////// Internal Logic //////////
///////////////////////////////////
blockCheck(){
if(this.blocked){
this.recalcAfterBlock = true;
}
return this.blocked;
}
visibleRows(viewable, rows){
if(this.topRow){
rows.unshift(this.topRow);
}
if(this.botRow){
rows.push(this.botRow);
}
return rows;
}
rowsUpdated(row){
if(this.table.options.groupBy){
this.recalcRowGroup(row);
}else {
this.recalcActiveRows();
}
}
recalcActiveRowsRefresh(){
if(this.table.options.groupBy && this.table.options.dataTreeStartExpanded && this.table.options.dataTree){
this.recalcAll();
}else {
this.recalcActiveRows();
}
}
recalcActiveRows(){
this.recalc(this.table.rowManager.activeRows);
}
cellValueChanged(cell){
if(cell.column.definition.topCalc || cell.column.definition.bottomCalc){
if(this.table.options.groupBy){
if(this.table.options.columnCalcs == "table" || this.table.options.columnCalcs == "both"){
this.recalcActiveRows();
}
if(this.table.options.columnCalcs != "table"){
this.recalcRowGroup(cell.row);
}
}else {
this.recalcActiveRows();
}
}
}
initializeColumnCheck(column){
if(column.definition.topCalc || column.definition.bottomCalc){
this.initializeColumn(column);
}
}
//initialize column calcs
initializeColumn(column){
var def = column.definition;
var config = {
topCalcParams:def.topCalcParams || {},
botCalcParams:def.bottomCalcParams || {},
};
if(def.topCalc){
switch(typeof def.topCalc){
case "string":
if(ColumnCalcs.calculations[def.topCalc]){
config.topCalc = ColumnCalcs.calculations[def.topCalc];
}else {
console.warn("Column Calc Error - No such calculation found, ignoring: ", def.topCalc);
}
break;
case "function":
config.topCalc = def.topCalc;
break;
}
if(config.topCalc){
column.modules.columnCalcs = config;
this.topCalcs.push(column);
if(this.table.options.columnCalcs != "group"){
this.initializeTopRow();
}
}
}
if(def.bottomCalc){
switch(typeof def.bottomCalc){
case "string":
if(ColumnCalcs.calculations[def.bottomCalc]){
config.botCalc = ColumnCalcs.calculations[def.bottomCalc];
}else {
console.warn("Column Calc Error - No such calculation found, ignoring: ", def.bottomCalc);
}
break;
case "function":
config.botCalc = def.bottomCalc;
break;
}
if(config.botCalc){
column.modules.columnCalcs = config;
this.botCalcs.push(column);
if(this.table.options.columnCalcs != "group"){
this.initializeBottomRow();
}
}
}
}
//dummy functions to handle being mock column manager
registerColumnField(){}
removeCalcs(){
var changed = false;
if(this.topInitialized){
this.topInitialized = false;
this.topElement.parentNode.removeChild(this.topElement);
changed = true;
}
if(this.botInitialized){
this.botInitialized = false;
this.footerRemove(this.botElement);
changed = true;
}
if(changed){
this.table.rowManager.adjustTableSize();
}
}
reinitializeCalcs(){
if(this.topCalcs.length){
this.initializeTopRow();
}
if(this.botCalcs.length){
this.initializeBottomRow();
}
}
initializeTopRow(){
if(!this.topInitialized){
this.table.columnManager.getContentsElement().insertBefore(this.topElement, this.table.columnManager.headersElement.nextSibling);
this.topInitialized = true;
}
}
initializeBottomRow(){
if(!this.botInitialized){
this.footerPrepend(this.botElement);
this.botInitialized = true;
}
}
scrollHorizontal(left){
if(this.botInitialized && this.botRow){
this.botElement.scrollLeft = left;
}
}
recalc(rows){
var data, row;
if(!this.blockCheck()){
if(this.topInitialized || this.botInitialized){
data = this.rowsToData(rows);
if(this.topInitialized){
if(this.topRow){
this.topRow.deleteCells();
}
row = this.generateRow("top", data);
this.topRow = row;
while(this.topElement.firstChild) this.topElement.removeChild(this.topElement.firstChild);
this.topElement.appendChild(row.getElement());
row.initialize(true);
}
if(this.botInitialized){
if(this.botRow){
this.botRow.deleteCells();
}
row = this.generateRow("bottom", data);
this.botRow = row;
while(this.botElement.firstChild) this.botElement.removeChild(this.botElement.firstChild);
this.botElement.appendChild(row.getElement());
row.initialize(true);
}
this.table.rowManager.adjustTableSize();
//set resizable handles
if(this.table.modExists("frozenColumns")){
this.table.modules.frozenColumns.layout();
}
}
}
}
recalcRowGroup(row){
this.recalcGroup(this.table.modules.groupRows.getRowGroup(row));
}
recalcAll(){
if(this.topCalcs.length || this.botCalcs.length){
if(this.table.options.columnCalcs !== "group"){
this.recalcActiveRows();
}
if(this.table.options.groupBy && this.table.options.columnCalcs !== "table"){
var groups = this.table.modules.groupRows.getChildGroups();
groups.forEach((group) => {
this.recalcGroup(group);
});
}
}
}
recalcGroup(group){
var data, rowData;
if(!this.blockCheck()){
if(group){
if(group.calcs){
if(group.calcs.bottom){
data = this.rowsToData(group.rows);
rowData = this.generateRowData("bottom", data);
group.calcs.bottom.updateData(rowData);
group.calcs.bottom.reinitialize();
}
if(group.calcs.top){
data = this.rowsToData(group.rows);
rowData = this.generateRowData("top", data);
group.calcs.top.updateData(rowData);
group.calcs.top.reinitialize();
}
}
}
}
}
//generate top stats row
generateTopRow(rows){
return this.generateRow("top", this.rowsToData(rows));
}
//generate bottom stats row
generateBottomRow(rows){
return this.generateRow("bottom", this.rowsToData(rows));
}
rowsToData(rows){
var data = [];
rows.forEach((row) => {
data.push(row.getData());
if(this.table.options.dataTree && this.table.options.dataTreeChildColumnCalcs){
if(row.modules.dataTree && row.modules.dataTree.open){
var children = this.rowsToData(this.table.modules.dataTree.getFilteredTreeChildren(row));
data = data.concat(children);
}
}
});
return data;
}
//generate stats row
generateRow(pos, data){
var rowData = this.generateRowData(pos, data),
row;
if(this.table.modExists("mutator")){
this.table.modules.mutator.disable();
}
row = new Row(rowData, this, "calc");
if(this.table.modExists("mutator")){
this.table.modules.mutator.enable();
}
row.getElement().classList.add("tabulator-calcs", "tabulator-calcs-" + pos);
row.component = false;
row.getComponent = () => {
if(!row.component){
row.component = new CalcComponent(row);
}
return row.component;
};
row.generateCells = () => {
var cells = [];
this.table.columnManager.columnsByIndex.forEach((column) => {
//set field name of mock column
this.genColumn.setField(column.getField());
this.genColumn.hozAlign = column.hozAlign;
if(column.definition[pos + "CalcFormatter"] && this.table.modExists("format")){
this.genColumn.modules.format = {
formatter: this.table.modules.format.getFormatter(column.definition[pos + "CalcFormatter"]),
params: column.definition[pos + "CalcFormatterParams"] || {},
};
}else {
this.genColumn.modules.format = {
formatter: this.table.modules.format.getFormatter("plaintext"),
params:{}
};
}
//ensure css class definition is replicated to calculation cell
this.genColumn.definition.cssClass = column.definition.cssClass;
//generate cell and assign to correct column
var cell = new Cell(this.genColumn, row);
cell.getElement();
cell.column = column;
cell.setWidth();
column.cells.push(cell);
cells.push(cell);
if(!column.visible){
cell.hide();
}
});
row.cells = cells;
};
return row;
}
//generate stats row
generateRowData(pos, data){
var rowData = {},
calcs = pos == "top" ? this.topCalcs : this.botCalcs,
type = pos == "top" ? "topCalc" : "botCalc",
params, paramKey;
calcs.forEach(function(column){
var values = [];
if(column.modules.columnCalcs && column.modules.columnCalcs[type]){
data.forEach(function(item){
values.push(column.getFieldValue(item));
});
paramKey = type + "Params";
params = typeof column.modules.columnCalcs[paramKey] === "function" ? column.modules.columnCalcs[paramKey](values, data) : column.modules.columnCalcs[paramKey];
column.setFieldValue(rowData, column.modules.columnCalcs[type](values, data, params));
}
});
return rowData;
}
hasTopCalcs(){
return !!(this.topCalcs.length);
}
hasBottomCalcs(){
return !!(this.botCalcs.length);
}
//handle table redraw
redraw(){
if(this.topRow){
this.topRow.normalizeHeight(true);
}
if(this.botRow){
this.botRow.normalizeHeight(true);
}
}
//return the calculated
getResults(){
var results = {},
groups;
if(this.table.options.groupBy && this.table.modExists("groupRows")){
groups = this.table.modules.groupRows.getGroups(true);
groups.forEach((group) => {
results[group.getKey()] = this.getGroupResults(group);
});
}else {
results = {
top: this.topRow ? this.topRow.getData() : {},
bottom: this.botRow ? this.botRow.getData() : {},
};
}
return results;
}
//get results from a group
getGroupResults(group){
var groupObj = group._getSelf(),
subGroups = group.getSubGroups(),
subGroupResults = {},
results = {};
subGroups.forEach((subgroup) => {
subGroupResults[subgroup.getKey()] = this.getGroupResults(subgroup);
});
results = {
top: groupObj.calcs.top ? groupObj.calcs.top.getData() : {},
bottom: groupObj.calcs.bottom ? groupObj.calcs.bottom.getData() : {},
groups: subGroupResults,
};
return results;
}
adjustForScrollbar(width){
if(this.botRow){
if(this.table.rtl){
this.botElement.style.paddingLeft = width + "px";
}else {
this.botElement.style.paddingRight = width + "px";
}
}
}
}
ColumnCalcs.moduleName = "columnCalcs";
//load defaults
ColumnCalcs.calculations = defaultCalculations;
class DataTree extends Module{
constructor(table){
super(table);
this.indent = 10;
this.field = "";
this.collapseEl = null;
this.expandEl = null;
this.branchEl = null;
this.elementField = false;
this.startOpen = function(){};
this.registerTableOption("dataTree", false); //enable data tree
this.registerTableOption("dataTreeFilter", true); //filter child rows
this.registerTableOption("dataTreeSort", true); //sort child rows
this.registerTableOption("dataTreeElementColumn", false);
this.registerTableOption("dataTreeBranchElement", true);//show data tree branch element
this.registerTableOption("dataTreeChildIndent", 9); //data tree child indent in px
this.registerTableOption("dataTreeChildField", "_children");//data tre column field to look for child rows
this.registerTableOption("dataTreeCollapseElement", false);//data tree row collapse element
this.registerTableOption("dataTreeExpandElement", false);//data tree row expand element
this.registerTableOption("dataTreeStartExpanded", false);
this.registerTableOption("dataTreeChildColumnCalcs", false);//include visible data tree rows in column calculations
this.registerTableOption("dataTreeSelectPropagate", false);//selecting a parent row selects its children
//register component functions
this.registerComponentFunction("row", "treeCollapse", this.collapseRow.bind(this));
this.registerComponentFunction("row", "treeExpand", this.expandRow.bind(this));
this.registerComponentFunction("row", "treeToggle", this.toggleRow.bind(this));
this.registerComponentFunction("row", "getTreeParent", this.getTreeParent.bind(this));
this.registerComponentFunction("row", "getTreeChildren", this.getRowChildren.bind(this));
this.registerComponentFunction("row", "addTreeChild", this.addTreeChildRow.bind(this));
this.registerComponentFunction("row", "isTreeExpanded", this.isRowExpanded.bind(this));
}
initialize(){
if(this.table.options.dataTree){
var dummyEl = null,
options = this.table.options;
this.field = options.dataTreeChildField;
this.indent = options.dataTreeChildIndent;
if(this.options("movableRows")){
console.warn("The movableRows option is not available with dataTree enabled, moving of child rows could result in unpredictable behavior");
}
if(options.dataTreeBranchElement){
if(options.dataTreeBranchElement === true){
this.branchEl = document.createElement("div");
this.branchEl.classList.add("tabulator-data-tree-branch");
}else {
if(typeof options.dataTreeBranchElement === "string"){
dummyEl = document.createElement("div");
dummyEl.innerHTML = options.dataTreeBranchElement;
this.branchEl = dummyEl.firstChild;
}else {
this.branchEl = options.dataTreeBranchElement;
}
}
}
if(options.dataTreeCollapseElement){
if(typeof options.dataTreeCollapseElement === "string"){
dummyEl = document.createElement("div");
dummyEl.innerHTML = options.dataTreeCollapseElement;
this.collapseEl = dummyEl.firstChild;
}else {
this.collapseEl = options.dataTreeCollapseElement;
}
}else {
this.collapseEl = document.createElement("div");
this.collapseEl.classList.add("tabulator-data-tree-control");
this.collapseEl.tabIndex = 0;
this.collapseEl.innerHTML = "
";
}
if(options.dataTreeExpandElement){
if(typeof options.dataTreeExpandElement === "string"){
dummyEl = document.createElement("div");
dummyEl.innerHTML = options.dataTreeExpandElement;
this.expandEl = dummyEl.firstChild;
}else {
this.expandEl = options.dataTreeExpandElement;
}
}else {
this.expandEl = document.createElement("div");
this.expandEl.classList.add("tabulator-data-tree-control");
this.expandEl.tabIndex = 0;
this.expandEl.innerHTML = "";
}
switch(typeof options.dataTreeStartExpanded){
case "boolean":
this.startOpen = function(row, index){
return options.dataTreeStartExpanded;
};
break;
case "function":
this.startOpen = options.dataTreeStartExpanded;
break;
default:
this.startOpen = function(row, index){
return options.dataTreeStartExpanded[index];
};
break;
}
this.subscribe("row-init", this.initializeRow.bind(this));
this.subscribe("row-layout-after", this.layoutRow.bind(this));
this.subscribe("row-deleted", this.rowDelete.bind(this),0);
this.subscribe("row-data-changed", this.rowDataChanged.bind(this), 10);
this.subscribe("cell-value-updated", this.cellValueChanged.bind(this));
this.subscribe("edit-cancelled", this.cellValueChanged.bind(this));
this.subscribe("column-moving-rows", this.columnMoving.bind(this));
this.subscribe("table-built", this.initializeElementField.bind(this));
this.subscribe("table-redrawing", this.tableRedrawing.bind(this));
this.registerDisplayHandler(this.getRows.bind(this), 30);
}
}
tableRedrawing(force){
var rows;
if(force){
rows = this.table.rowManager.getRows();
rows.forEach((row) => {
this.reinitializeRowChildren(row);
});
}
}
initializeElementField(){
var firstCol = this.table.columnManager.getFirstVisibleColumn();
this.elementField = this.table.options.dataTreeElementColumn || (firstCol ? firstCol.field : false);
}
getRowChildren(row){
return this.getTreeChildren(row, true);
}
columnMoving(){
var rows = [];
this.table.rowManager.rows.forEach((row) => {
rows = rows.concat(this.getTreeChildren(row, false, true));
});
return rows;
}
rowDataChanged(row, visible, updatedData){
if(this.redrawNeeded(updatedData)){
this.initializeRow(row);
if(visible){
this.layoutRow(row);
this.refreshData(true);
}
}
}
cellValueChanged(cell){
var field = cell.column.getField();
if(field === this.elementField){
this.layoutRow(cell.row);
}
}
initializeRow(row){
var childArray = row.getData()[this.field];
var isArray = Array.isArray(childArray);
var children = isArray || (!isArray && typeof childArray === "object" && childArray !== null);
if(!children && row.modules.dataTree && row.modules.dataTree.branchEl){
row.modules.dataTree.branchEl.parentNode.removeChild(row.modules.dataTree.branchEl);
}
if(!children && row.modules.dataTree && row.modules.dataTree.controlEl){
row.modules.dataTree.controlEl.parentNode.removeChild(row.modules.dataTree.controlEl);
}
row.modules.dataTree = {
index: row.modules.dataTree ? row.modules.dataTree.index : 0,
open: children ? (row.modules.dataTree ? row.modules.dataTree.open : this.startOpen(row.getComponent(), 0)) : false,
controlEl: row.modules.dataTree && children ? row.modules.dataTree.controlEl : false,
branchEl: row.modules.dataTree && children ? row.modules.dataTree.branchEl : false,
parent: row.modules.dataTree ? row.modules.dataTree.parent : false,
children:children,
};
}
reinitializeRowChildren(row){
var children = this.getTreeChildren(row, false, true);
children.forEach(function(child){
child.reinitialize(true);
});
}
layoutRow(row){
var cell = this.elementField ? row.getCell(this.elementField) : row.getCells()[0],
el = cell.getElement(),
config = row.modules.dataTree;
if(config.branchEl){
if(config.branchEl.parentNode){
config.branchEl.parentNode.removeChild(config.branchEl);
}
config.branchEl = false;
}
if(config.controlEl){
if(config.controlEl.parentNode){
config.controlEl.parentNode.removeChild(config.controlEl);
}
config.controlEl = false;
}
this.generateControlElement(row, el);
row.getElement().classList.add("tabulator-tree-level-" + config.index);
if(config.index){
if(this.branchEl){
config.branchEl = this.branchEl.cloneNode(true);
el.insertBefore(config.branchEl, el.firstChild);
if(this.table.rtl){
config.branchEl.style.marginRight = (((config.branchEl.offsetWidth + config.branchEl.style.marginLeft) * (config.index - 1)) + (config.index * this.indent)) + "px";
}else {
config.branchEl.style.marginLeft = (((config.branchEl.offsetWidth + config.branchEl.style.marginRight) * (config.index - 1)) + (config.index * this.indent)) + "px";
}
}else {
if(this.table.rtl){
el.style.paddingRight = parseInt(window.getComputedStyle(el, null).getPropertyValue('padding-right')) + (config.index * this.indent) + "px";
}else {
el.style.paddingLeft = parseInt(window.getComputedStyle(el, null).getPropertyValue('padding-left')) + (config.index * this.indent) + "px";
}
}
}
}
generateControlElement(row, el){
var config = row.modules.dataTree,
oldControl = config.controlEl;
el = el || row.getCells()[0].getElement();
if(config.children !== false){
if(config.open){
config.controlEl = this.collapseEl.cloneNode(true);
config.controlEl.addEventListener("click", (e) => {
e.stopPropagation();
this.collapseRow(row);
});
}else {
config.controlEl = this.expandEl.cloneNode(true);
config.controlEl.addEventListener("click", (e) => {
e.stopPropagation();
this.expandRow(row);
});
}
config.controlEl.addEventListener("mousedown", (e) => {
e.stopPropagation();
});
if(oldControl && oldControl.parentNode === el){
oldControl.parentNode.replaceChild(config.controlEl,oldControl);
}else {
el.insertBefore(config.controlEl, el.firstChild);
}
}
}
getRows(rows){
var output = [];
rows.forEach((row, i) => {
var config, children;
output.push(row);
if(row instanceof Row){
row.create();
config = row.modules.dataTree.children;
if(!config.index && config.children !== false){
children = this.getChildren(row);
children.forEach((child) => {
child.create();
output.push(child);
});
}
}
});
return output;
}
getChildren(row, allChildren){
var config = row.modules.dataTree,
children = [],
output = [];
if(config.children !== false && (config.open || allChildren)){
if(!Array.isArray(config.children)){
config.children = this.generateChildren(row);
}
if(this.table.modExists("filter") && this.table.options.dataTreeFilter){
children = this.table.modules.filter.filter(config.children);
}else {
children = config.children;
}
if(this.table.modExists("sort") && this.table.options.dataTreeSort){
this.table.modules.sort.sort(children);
}
children.forEach((child) => {
output.push(child);
var subChildren = this.getChildren(child);
subChildren.forEach((sub) => {
output.push(sub);
});
});
}
return output;
}
generateChildren(row){
var children = [];
var childArray = row.getData()[this.field];
if(!Array.isArray(childArray)){
childArray = [childArray];
}
childArray.forEach((childData) => {
var childRow = new Row(childData || {}, this.table.rowManager);
childRow.create();
childRow.modules.dataTree.index = row.modules.dataTree.index + 1;
childRow.modules.dataTree.parent = row;
if(childRow.modules.dataTree.children){
childRow.modules.dataTree.open = this.startOpen(childRow.getComponent(), childRow.modules.dataTree.index);
}
children.push(childRow);
});
return children;
}
expandRow(row, silent){
var config = row.modules.dataTree;
if(config.children !== false){
config.open = true;
row.reinitialize();
this.refreshData(true);
this.dispatchExternal("dataTreeRowExpanded", row.getComponent(), row.modules.dataTree.index);
}
}
collapseRow(row){
var config = row.modules.dataTree;
if(config.children !== false){
config.open = false;
row.reinitialize();
this.refreshData(true);
this.dispatchExternal("dataTreeRowCollapsed", row.getComponent(), row.modules.dataTree.index);
}
}
toggleRow(row){
var config = row.modules.dataTree;
if(config.children !== false){
if(config.open){
this.collapseRow(row);
}else {
this.expandRow(row);
}
}
}
isRowExpanded(row){
return row.modules.dataTree.open;
}
getTreeParent(row){
return row.modules.dataTree.parent ? row.modules.dataTree.parent.getComponent() : false;
}
getTreeParentRoot(row){
return row.modules.dataTree && row.modules.dataTree.parent ? this.getTreeParentRoot(row.modules.dataTree.parent) : row;
}
getFilteredTreeChildren(row){
var config = row.modules.dataTree,
output = [], children;
if(config.children){
if(!Array.isArray(config.children)){
config.children = this.generateChildren(row);
}
if(this.table.modExists("filter") && this.table.options.dataTreeFilter){
children = this.table.modules.filter.filter(config.children);
}else {
children = config.children;
}
children.forEach((childRow) => {
if(childRow instanceof Row){
output.push(childRow);
}
});
}
return output;
}
rowDelete(row){
var parent = row.modules.dataTree.parent,
childIndex;
if(parent){
childIndex = this.findChildIndex(row, parent);
if(childIndex !== false){
parent.data[this.field].splice(childIndex, 1);
}
if(!parent.data[this.field].length){
delete parent.data[this.field];
}
this.initializeRow(parent);
this.layoutRow(parent);
}
this.refreshData(true);
}
addTreeChildRow(row, data, top, index){
var childIndex = false;
if(typeof data === "string"){
data = JSON.parse(data);
}
if(!Array.isArray(row.data[this.field])){
row.data[this.field] = [];
row.modules.dataTree.open = this.startOpen(row.getComponent(), row.modules.dataTree.index);
}
if(typeof index !== "undefined"){
childIndex = this.findChildIndex(index, row);
if(childIndex !== false){
row.data[this.field].splice((top ? childIndex : childIndex + 1), 0, data);
}
}
if(childIndex === false){
if(top){
row.data[this.field].unshift(data);
}else {
row.data[this.field].push(data);
}
}
this.initializeRow(row);
this.layoutRow(row);
this.refreshData(true);
}
findChildIndex(subject, parent){
var match = false;
if(typeof subject == "object"){
if(subject instanceof Row){
//subject is row element
match = subject.data;
}else if(subject instanceof RowComponent){
//subject is public row component
match = subject._getSelf().data;
}else if(typeof HTMLElement !== "undefined" && subject instanceof HTMLElement){
if(parent.modules.dataTree){
match = parent.modules.dataTree.children.find((childRow) => {
return childRow instanceof Row ? childRow.element === subject : false;
});
if(match){
match = match.data;
}
}
}else if(subject === null){
match = false;
}
}else if(typeof subject == "undefined"){
match = false;
}else {
//subject should be treated as the index of the row
match = parent.data[this.field].find((row) => {
return row.data[this.table.options.index] == subject;
});
}
if(match){
if(Array.isArray(parent.data[this.field])){
match = parent.data[this.field].indexOf(match);
}
if(match == -1){
match = false;
}
}
//catch all for any other type of input
return match;
}
getTreeChildren(row, component, recurse){
var config = row.modules.dataTree,
output = [];
if(config.children){
if(!Array.isArray(config.children)){
config.children = this.generateChildren(row);
}
config.children.forEach((childRow) => {
if(childRow instanceof Row){
output.push(component ? childRow.getComponent() : childRow);
if(recurse){
output = output.concat(this.getTreeChildren(childRow, component, recurse));
}
}
});
}
return output;
}
getChildField(){
return this.field;
}
redrawNeeded(data){
return (this.field ? typeof data[this.field] !== "undefined" : false) || (this.elementField ? typeof data[this.elementField] !== "undefined" : false);
}
}
DataTree.moduleName = "dataTree";
function csv(list, options = {}, setFileContents){
var delimiter = options.delimiter ? options.delimiter : ",",
fileContents = [],
headers = [];
list.forEach((row) => {
var item = [];
switch(row.type){
case "group":
console.warn("Download Warning - CSV downloader cannot process row groups");
break;
case "calc":
console.warn("Download Warning - CSV downloader cannot process column calculations");
break;
case "header":
row.columns.forEach((col, i) => {
if(col && col.depth === 1){
headers[i] = typeof col.value == "undefined" || col.value === null ? "" : ('"' + String(col.value).split('"').join('""') + '"');
}
});
break;
case "row":
row.columns.forEach((col) => {
if(col){
switch(typeof col.value){
case "object":
col.value = col.value !== null ? JSON.stringify(col.value) : "";
break;
case "undefined":
col.value = "";
break;
}
item.push('"' + String(col.value).split('"').join('""') + '"');
}
});
fileContents.push(item.join(delimiter));
break;
}
});
if(headers.length){
fileContents.unshift(headers.join(delimiter));
}
fileContents = fileContents.join("\n");
if(options.bom){
fileContents = "\ufeff" + fileContents;
}
setFileContents(fileContents, "text/csv");
}
function json(list, options, setFileContents){
var fileContents = [];
list.forEach((row) => {
var item = {};
switch(row.type){
case "header":
break;
case "group":
console.warn("Download Warning - JSON downloader cannot process row groups");
break;
case "calc":
console.warn("Download Warning - JSON downloader cannot process column calculations");
break;
case "row":
row.columns.forEach((col) => {
if(col){
item[col.component.getTitleDownload() || col.component.getField()] = col.value;
}
});
fileContents.push(item);
break;
}
});
fileContents = JSON.stringify(fileContents, null, '\t');
setFileContents(fileContents, "application/json");
}
function pdf(list, options = {}, setFileContents){
var header = [],
body = [],
autoTableParams = {},
rowGroupStyles = options.rowGroupStyles || {
fontStyle: "bold",
fontSize: 12,
cellPadding: 6,
fillColor: 220,
},
rowCalcStyles = options.rowCalcStyles || {
fontStyle: "bold",
fontSize: 10,
cellPadding: 4,
fillColor: 232,
},
jsPDFParams = options.jsPDF || {},
title = options.title ? options.title : "";
if(!jsPDFParams.orientation){
jsPDFParams.orientation = options.orientation || "landscape";
}
if(!jsPDFParams.unit){
jsPDFParams.unit = "pt";
}
//parse row list
list.forEach((row) => {
switch(row.type){
case "header":
header.push(parseRow(row));
break;
case "group":
body.push(parseRow(row, rowGroupStyles));
break;
case "calc":
body.push(parseRow(row, rowCalcStyles));
break;
case "row":
body.push(parseRow(row));
break;
}
});
function parseRow(row, styles){
var rowData = [];
row.columns.forEach((col) =>{
var cell;
if(col){
switch(typeof col.value){
case "object":
col.value = col.value !== null ? JSON.stringify(col.value) : "";
break;
case "undefined":
col.value = "";
break;
}
cell = {
content:col.value,
colSpan:col.width,
rowSpan:col.height,
};
if(styles){
cell.styles = styles;
}
rowData.push(cell);
}
});
return rowData;
}
//configure PDF
var doc = new jspdf.jsPDF(jsPDFParams); //set document to landscape, better for most tables
if(options.autoTable){
if(typeof options.autoTable === "function"){
autoTableParams = options.autoTable(doc) || {};
}else {
autoTableParams = options.autoTable;
}
}
if(title){
autoTableParams.didDrawPage = function(data) {
doc.text(title, 40, 30);
};
}
autoTableParams.head = header;
autoTableParams.body = body;
doc.autoTable(autoTableParams);
if(options.documentProcessing){
options.documentProcessing(doc);
}
setFileContents(doc.output("arraybuffer"), "application/pdf");
}
function xlsx(list, options, setFileContents){
var self = this,
sheetName = options.sheetName || "Sheet1",
workbook = XLSX.utils.book_new(),
tableFeatures = new CoreFeature(this),
compression = 'compress' in options ? options.compress : true,
output;
workbook.SheetNames = [];
workbook.Sheets = {};
function generateSheet(){
var rows = [],
merges = [],
worksheet = {},
range = {s: {c:0, r:0}, e: {c:(list[0] ? list[0].columns.reduce((a, b) => a + (b && b.width ? b.width : 1), 0) : 0), r:list.length }};
//parse row list
list.forEach((row, i) => {
var rowData = [];
row.columns.forEach(function(col, j){
if(col){
rowData.push(!(col.value instanceof Date) && typeof col.value === "object" ? JSON.stringify(col.value) : col.value);
if(col.width > 1 || col.height > -1){
if(col.height > 1 || col.width > 1){
merges.push({s:{r:i,c:j},e:{r:i + col.height - 1,c:j + col.width - 1}});
}
}
}else {
rowData.push("");
}
});
rows.push(rowData);
});
//convert rows to worksheet
XLSX.utils.sheet_add_aoa(worksheet, rows);
worksheet['!ref'] = XLSX.utils.encode_range(range);
if(merges.length){
worksheet["!merges"] = merges;
}
return worksheet;
}
if(options.sheetOnly){
setFileContents(generateSheet());
return;
}
if(options.sheets){
for(var sheet in options.sheets){
if(options.sheets[sheet] === true){
workbook.SheetNames.push(sheet);
workbook.Sheets[sheet] = generateSheet();
}else {
workbook.SheetNames.push(sheet);
tableFeatures.commsSend(options.sheets[sheet], "download", "intercept",{
type:"xlsx",
options:{sheetOnly:true},
active:self.active,
intercept:function(data){
workbook.Sheets[sheet] = data;
}
});
}
}
}else {
workbook.SheetNames.push(sheetName);
workbook.Sheets[sheetName] = generateSheet();
}
if(options.documentProcessing){
workbook = options.documentProcessing(workbook);
}
//convert workbook to binary array
function s2ab(s) {
var buf = new ArrayBuffer(s.length);
var view = new Uint8Array(buf);
for (var i=0; i!=s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF;
return buf;
}
output = XLSX.write(workbook, {bookType:'xlsx', bookSST:true, type: 'binary', compression });
setFileContents(s2ab(output), "application/octet-stream");
}
function html(list, options, setFileContents){
if(this.modExists("export", true)){
setFileContents(this.modules.export.generateHTMLTable(list), "text/html");
}
}
function jsonLines (list, options, setFileContents) {
const fileContents = [];
list.forEach((row) => {
const item = {};
switch (row.type) {
case "header":
break;
case "group":
console.warn("Download Warning - JSON downloader cannot process row groups");
break;
case "calc":
console.warn("Download Warning - JSON downloader cannot process column calculations");
break;
case "row":
row.columns.forEach((col) => {
if (col) {
item[col.component.getTitleDownload() || col.component.getField()] = col.value;
}
});
fileContents.push(JSON.stringify(item));
break;
}
});
setFileContents(fileContents.join("\n"), "application/x-ndjson");
}
var defaultDownloaders = {
csv:csv,
json:json,
jsonLines:jsonLines,
pdf:pdf,
xlsx:xlsx,
html:html,
};
class Download extends Module{
constructor(table){
super(table);
this.registerTableOption("downloadEncoder", function(data, mimeType){
return new Blob([data],{type:mimeType});
}); //function to manipulate download data
this.registerTableOption("downloadReady", undefined); //warn of function deprecation
this.registerTableOption("downloadConfig", {}); //download config
this.registerTableOption("downloadRowRange", "active"); //restrict download to active rows only
this.registerColumnOption("download");
this.registerColumnOption("titleDownload");
}
initialize(){
this.deprecatedOptionsCheck();
this.registerTableFunction("download", this.download.bind(this));
this.registerTableFunction("downloadToTab", this.downloadToTab.bind(this));
}
deprecatedOptionsCheck(){
this.deprecationCheck("downloadReady", "downloadEncoder");
}
///////////////////////////////////
///////// Table Functions /////////
///////////////////////////////////
downloadToTab(type, filename, options, active){
this.download(type, filename, options, active, true);
}
///////////////////////////////////
///////// Internal Logic //////////
///////////////////////////////////
//trigger file download
download(type, filename, options, range, interceptCallback){
var downloadFunc = false;
function buildLink(data, mime){
if(interceptCallback){
if(interceptCallback === true){
this.triggerDownload(data, mime, type, filename, true);
}else {
interceptCallback(data);
}
}else {
this.triggerDownload(data, mime, type, filename);
}
}
if(typeof type == "function"){
downloadFunc = type;
}else {
if(Download.downloaders[type]){
downloadFunc = Download.downloaders[type];
}else {
console.warn("Download Error - No such download type found: ", type);
}
}
if(downloadFunc){
var list = this.generateExportList(range);
downloadFunc.call(this.table, list , options || {}, buildLink.bind(this));
}
}
generateExportList(range){
var list = this.table.modules.export.generateExportList(this.table.options.downloadConfig, false, range || this.table.options.downloadRowRange, "download");
//assign group header formatter
var groupHeader = this.table.options.groupHeaderDownload;
if(groupHeader && !Array.isArray(groupHeader)){
groupHeader = [groupHeader];
}
list.forEach((row) => {
var group;
if(row.type === "group"){
group = row.columns[0];
if(groupHeader && groupHeader[row.indent]){
group.value = groupHeader[row.indent](group.value, row.component._group.getRowCount(), row.component._group.getData(), row.component);
}
}
});
return list;
}
triggerDownload(data, mime, type, filename, newTab){
var element = document.createElement('a'),
blob = this.table.options.downloadEncoder(data, mime);
if(blob){
if(newTab){
window.open(window.URL.createObjectURL(blob));
}else {
filename = filename || "Tabulator." + (typeof type === "function" ? "txt" : type);
if(navigator.msSaveOrOpenBlob){
navigator.msSaveOrOpenBlob(blob, filename);
}else {
element.setAttribute('href', window.URL.createObjectURL(blob));
//set file title
element.setAttribute('download', filename);
//trigger download
element.style.display = 'none';
document.body.appendChild(element);
element.click();
//remove temporary link element
document.body.removeChild(element);
}
}
this.dispatchExternal("downloadComplete");
}
}
commsReceived(table, action, data){
switch(action){
case "intercept":
this.download(data.type, "", data.options, data.active, data.intercept);
break;
}
}
}
Download.moduleName = "download";
//load defaults
Download.downloaders = defaultDownloaders;
function maskInput(el, options){
var mask = options.mask,
maskLetter = typeof options.maskLetterChar !== "undefined" ? options.maskLetterChar : "A",
maskNumber = typeof options.maskNumberChar !== "undefined" ? options.maskNumberChar : "9",
maskWildcard = typeof options.maskWildcardChar !== "undefined" ? options.maskWildcardChar : "*";
function fillSymbols(index){
var symbol = mask[index];
if(typeof symbol !== "undefined" && symbol !== maskWildcard && symbol !== maskLetter && symbol !== maskNumber){
el.value = el.value + "" + symbol;
fillSymbols(index+1);
}
}
el.addEventListener("keydown", (e) => {
var index = el.value.length,
char = e.key;
if(e.keyCode > 46 && !e.ctrlKey && !e.metaKey){
if(index >= mask.length){
e.preventDefault();
e.stopPropagation();
return false;
}else {
switch(mask[index]){
case maskLetter:
if(char.toUpperCase() == char.toLowerCase()){
e.preventDefault();
e.stopPropagation();
return false;
}
break;
case maskNumber:
if(isNaN(char)){
e.preventDefault();
e.stopPropagation();
return false;
}
break;
case maskWildcard:
break;
default:
if(char !== mask[index]){
e.preventDefault();
e.stopPropagation();
return false;
}
}
}
}
return;
});
el.addEventListener("keyup", (e) => {
if(e.keyCode > 46){
if(options.maskAutoFill){
fillSymbols(el.value.length);
}
}
});
if(!el.placeholder){
el.placeholder = mask;
}
if(options.maskAutoFill){
fillSymbols(el.value.length);
}
}
//input element
function input(cell, onRendered, success, cancel, editorParams){
//create and style input
var cellValue = cell.getValue(),
input = document.createElement("input");
input.setAttribute("type", editorParams.search ? "search" : "text");
input.style.padding = "4px";
input.style.width = "100%";
input.style.boxSizing = "border-box";
if(editorParams.elementAttributes && typeof editorParams.elementAttributes == "object"){
for (let key in editorParams.elementAttributes){
if(key.charAt(0) == "+"){
key = key.slice(1);
input.setAttribute(key, input.getAttribute(key) + editorParams.elementAttributes["+" + key]);
}else {
input.setAttribute(key, editorParams.elementAttributes[key]);
}
}
}
input.value = typeof cellValue !== "undefined" ? cellValue : "";
onRendered(function(){
if(cell._getSelf){
input.focus({preventScroll: true});
input.style.height = "100%";
if(editorParams.selectContents){
input.select();
}
}
});
function onChange(e){
if(((cellValue === null || typeof cellValue === "undefined") && input.value !== "") || input.value !== cellValue){
if(success(input.value)){
cellValue = input.value; //persist value if successfully validated incase editor is used as header filter
}
}else {
cancel();
}
}
//submit new value on blur or change
input.addEventListener("change", onChange);
input.addEventListener("blur", onChange);
//submit new value on enter
input.addEventListener("keydown", function(e){
switch(e.keyCode){
// case 9:
case 13:
onChange();
break;
case 27:
cancel();
break;
case 35:
case 36:
e.stopPropagation();
break;
}
});
if(editorParams.mask){
maskInput(input, editorParams);
}
return input;
}
//resizable text area element
function textarea(cell, onRendered, success, cancel, editorParams){
var cellValue = cell.getValue(),
vertNav = editorParams.verticalNavigation || "hybrid",
value = String(cellValue !== null && typeof cellValue !== "undefined" ? cellValue : ""),
input = document.createElement("textarea"),
scrollHeight = 0;
//create and style input
input.style.display = "block";
input.style.padding = "2px";
input.style.height = "100%";
input.style.width = "100%";
input.style.boxSizing = "border-box";
input.style.whiteSpace = "pre-wrap";
input.style.resize = "none";
if(editorParams.elementAttributes && typeof editorParams.elementAttributes == "object"){
for (let key in editorParams.elementAttributes){
if(key.charAt(0) == "+"){
key = key.slice(1);
input.setAttribute(key, input.getAttribute(key) + editorParams.elementAttributes["+" + key]);
}else {
input.setAttribute(key, editorParams.elementAttributes[key]);
}
}
}
input.value = value;
onRendered(function(){
if(cell._getSelf){
input.focus({preventScroll: true});
input.style.height = "100%";
input.scrollHeight;
input.style.height = input.scrollHeight + "px";
cell.getRow().normalizeHeight();
if(editorParams.selectContents){
input.select();
}
}
});
function onChange(e){
if(((cellValue === null || typeof cellValue === "undefined") && input.value !== "") || input.value !== cellValue){
if(success(input.value)){
cellValue = input.value; //persist value if successfully validated incase editor is used as header filter
}
setTimeout(function(){
cell.getRow().normalizeHeight();
},300);
}else {
cancel();
}
}
//submit new value on blur or change
input.addEventListener("change", onChange);
input.addEventListener("blur", onChange);
input.addEventListener("keyup", function(){
input.style.height = "";
var heightNow = input.scrollHeight;
input.style.height = heightNow + "px";
if(heightNow != scrollHeight){
scrollHeight = heightNow;
cell.getRow().normalizeHeight();
}
});
input.addEventListener("keydown", function(e){
switch(e.keyCode){
case 13:
if(e.shiftKey && editorParams.shiftEnterSubmit){
onChange();
}
break;
case 27:
cancel();
break;
case 38: //up arrow
if(vertNav == "editor" || (vertNav == "hybrid" && input.selectionStart)){
e.stopImmediatePropagation();
e.stopPropagation();
}
break;
case 40: //down arrow
if(vertNav == "editor" || (vertNav == "hybrid" && input.selectionStart !== input.value.length)){
e.stopImmediatePropagation();
e.stopPropagation();
}
break;
case 35:
case 36:
e.stopPropagation();
break;
}
});
if(editorParams.mask){
maskInput(input, editorParams);
}
return input;
}
//input element with type of number
function number(cell, onRendered, success, cancel, editorParams){
var cellValue = cell.getValue(),
vertNav = editorParams.verticalNavigation || "editor",
input = document.createElement("input");
input.setAttribute("type", "number");
if(typeof editorParams.max != "undefined"){
input.setAttribute("max", editorParams.max);
}
if(typeof editorParams.min != "undefined"){
input.setAttribute("min", editorParams.min);
}
if(typeof editorParams.step != "undefined"){
input.setAttribute("step", editorParams.step);
}
//create and style input
input.style.padding = "4px";
input.style.width = "100%";
input.style.boxSizing = "border-box";
if(editorParams.elementAttributes && typeof editorParams.elementAttributes == "object"){
for (let key in editorParams.elementAttributes){
if(key.charAt(0) == "+"){
key = key.slice(1);
input.setAttribute(key, input.getAttribute(key) + editorParams.elementAttributes["+" + key]);
}else {
input.setAttribute(key, editorParams.elementAttributes[key]);
}
}
}
input.value = cellValue;
var blurFunc = function(e){
onChange();
};
onRendered(function () {
if(cell._getSelf){
//submit new value on blur
input.removeEventListener("blur", blurFunc);
input.focus({preventScroll: true});
input.style.height = "100%";
//submit new value on blur
input.addEventListener("blur", blurFunc);
if(editorParams.selectContents){
input.select();
}
}
});
function onChange(){
var value = input.value;
if(!isNaN(value) && value !==""){
value = Number(value);
}
if(value !== cellValue){
if(success(value)){
cellValue = value; //persist value if successfully validated incase editor is used as header filter
}
}else {
cancel();
}
}
//submit new value on enter
input.addEventListener("keydown", function(e){
switch(e.keyCode){
case 13:
// case 9:
onChange();
break;
case 27:
cancel();
break;
case 38: //up arrow
case 40: //down arrow
if(vertNav == "editor"){
e.stopImmediatePropagation();
e.stopPropagation();
}
break;
case 35:
case 36:
e.stopPropagation();
break;
}
});
if(editorParams.mask){
maskInput(input, editorParams);
}
return input;
}
//input element with type of number
function range(cell, onRendered, success, cancel, editorParams){
var cellValue = cell.getValue(),
input = document.createElement("input");
input.setAttribute("type", "range");
if (typeof editorParams.max != "undefined") {
input.setAttribute("max", editorParams.max);
}
if (typeof editorParams.min != "undefined") {
input.setAttribute("min", editorParams.min);
}
if (typeof editorParams.step != "undefined") {
input.setAttribute("step", editorParams.step);
}
//create and style input
input.style.padding = "4px";
input.style.width = "100%";
input.style.boxSizing = "border-box";
if(editorParams.elementAttributes && typeof editorParams.elementAttributes == "object"){
for (let key in editorParams.elementAttributes){
if(key.charAt(0) == "+"){
key = key.slice(1);
input.setAttribute(key, input.getAttribute(key) + editorParams.elementAttributes["+" + key]);
}else {
input.setAttribute(key, editorParams.elementAttributes[key]);
}
}
}
input.value = cellValue;
onRendered(function () {
if(cell._getSelf){
input.focus({preventScroll: true});
input.style.height = "100%";
}
});
function onChange(){
var value = input.value;
if(!isNaN(value) && value !==""){
value = Number(value);
}
if(value != cellValue){
if(success(value)){
cellValue = value; //persist value if successfully validated incase editor is used as header filter
}
}else {
cancel();
}
}
//submit new value on blur
input.addEventListener("blur", function(e){
onChange();
});
//submit new value on enter
input.addEventListener("keydown", function(e){
switch(e.keyCode){
case 13:
// case 9:
onChange();
break;
case 27:
cancel();
break;
}
});
return input;
}
//input element
function date(cell, onRendered, success, cancel, editorParams){
var inputFormat = editorParams.format,
vertNav = editorParams.verticalNavigation || "editor",
DT = inputFormat ? (window.DateTime || luxon.DateTime) : null;
//create and style input
var cellValue = cell.getValue(),
input = document.createElement("input");
function convertDate(value){
var newDatetime;
if(DT.isDateTime(value)){
newDatetime = value;
}else if(inputFormat === "iso"){
newDatetime = DT.fromISO(String(value));
}else {
newDatetime = DT.fromFormat(String(value), inputFormat);
}
return newDatetime.toFormat("yyyy-MM-dd");
}
input.type = "date";
input.style.padding = "4px";
input.style.width = "100%";
input.style.boxSizing = "border-box";
if(editorParams.max){
input.setAttribute("max", inputFormat ? convertDate(editorParams.max) : editorParams.max);
}
if(editorParams.min){
input.setAttribute("min", inputFormat ? convertDate(editorParams.min) : editorParams.min);
}
if(editorParams.elementAttributes && typeof editorParams.elementAttributes == "object"){
for (let key in editorParams.elementAttributes){
if(key.charAt(0) == "+"){
key = key.slice(1);
input.setAttribute(key, input.getAttribute(key) + editorParams.elementAttributes["+" + key]);
}else {
input.setAttribute(key, editorParams.elementAttributes[key]);
}
}
}
cellValue = typeof cellValue !== "undefined" ? cellValue : "";
if(inputFormat){
if(DT){
cellValue = convertDate(cellValue);
}else {
console.error("Editor Error - 'date' editor 'format' param is dependant on luxon.js");
}
}
input.value = cellValue;
onRendered(function(){
if(cell._getSelf){
input.focus({preventScroll: true});
input.style.height = "100%";
if(editorParams.selectContents){
input.select();
}
}
});
function onChange(){
var value = input.value,
luxDate;
if(((cellValue === null || typeof cellValue === "undefined") && value !== "") || value !== cellValue){
if(value && inputFormat){
luxDate = DT.fromFormat(String(value), "yyyy-MM-dd");
switch(inputFormat){
case true:
value = luxDate;
break;
case "iso":
value = luxDate.toISO();
break;
default:
value = luxDate.toFormat(inputFormat);
}
}
if(success(value)){
cellValue = input.value; //persist value if successfully validated incase editor is used as header filter
}
}else {
cancel();
}
}
//submit new value on blur
input.addEventListener("blur", function(e) {
if (e.relatedTarget || e.rangeParent || e.explicitOriginalTarget !== input) {
onChange(); // only on a "true" blur; not when focusing browser's date/time picker
}
});
//submit new value on enter
input.addEventListener("keydown", function(e){
switch(e.keyCode){
// case 9:
case 13:
onChange();
break;
case 27:
cancel();
break;
case 35:
case 36:
e.stopPropagation();
break;
case 38: //up arrow
case 40: //down arrow
if(vertNav == "editor"){
e.stopImmediatePropagation();
e.stopPropagation();
}
break;
}
});
return input;
}
//input element
function time(cell, onRendered, success, cancel, editorParams){
var inputFormat = editorParams.format,
vertNav = editorParams.verticalNavigation || "editor",
DT = inputFormat ? (window.DateTime || luxon.DateTime) : null,
newDatetime;
//create and style input
var cellValue = cell.getValue(),
input = document.createElement("input");
input.type = "time";
input.style.padding = "4px";
input.style.width = "100%";
input.style.boxSizing = "border-box";
if(editorParams.elementAttributes && typeof editorParams.elementAttributes == "object"){
for (let key in editorParams.elementAttributes){
if(key.charAt(0) == "+"){
key = key.slice(1);
input.setAttribute(key, input.getAttribute(key) + editorParams.elementAttributes["+" + key]);
}else {
input.setAttribute(key, editorParams.elementAttributes[key]);
}
}
}
cellValue = typeof cellValue !== "undefined" ? cellValue : "";
if(inputFormat){
if(DT){
if(DT.isDateTime(cellValue)){
newDatetime = cellValue;
}else if(inputFormat === "iso"){
newDatetime = DT.fromISO(String(cellValue));
}else {
newDatetime = DT.fromFormat(String(cellValue), inputFormat);
}
cellValue = newDatetime.toFormat("hh:mm");
}else {
console.error("Editor Error - 'date' editor 'format' param is dependant on luxon.js");
}
}
input.value = cellValue;
onRendered(function(){
if(cell._getSelf){
input.focus({preventScroll: true});
input.style.height = "100%";
if(editorParams.selectContents){
input.select();
}
}
});
function onChange(){
var value = input.value,
luxTime;
if(((cellValue === null || typeof cellValue === "undefined") && value !== "") || value !== cellValue){
if(value && inputFormat){
luxTime = DT.fromFormat(String(value), "hh:mm");
switch(inputFormat){
case true:
value = luxTime;
break;
case "iso":
value = luxTime.toISO();
break;
default:
value = luxTime.toFormat(inputFormat);
}
}
if(success(value)){
cellValue = input.value; //persist value if successfully validated incase editor is used as header filter
}
}else {
cancel();
}
}
//submit new value on blur
input.addEventListener("blur", function(e) {
if (e.relatedTarget || e.rangeParent || e.explicitOriginalTarget !== input) {
onChange(); // only on a "true" blur; not when focusing browser's date/time picker
}
});
//submit new value on enter
input.addEventListener("keydown", function(e){
switch(e.keyCode){
// case 9:
case 13:
onChange();
break;
case 27:
cancel();
break;
case 35:
case 36:
e.stopPropagation();
break;
case 38: //up arrow
case 40: //down arrow
if(vertNav == "editor"){
e.stopImmediatePropagation();
e.stopPropagation();
}
break;
}
});
return input;
}
//input element
function datetime(cell, onRendered, success, cancel, editorParams){
var inputFormat = editorParams.format,
vertNav = editorParams.verticalNavigation || "editor",
DT = inputFormat ? (window.DateTime || luxon.DateTime) : null,
newDatetime;
//create and style input
var cellValue = cell.getValue(),
input = document.createElement("input");
input.type = "datetime-local";
input.style.padding = "4px";
input.style.width = "100%";
input.style.boxSizing = "border-box";
if(editorParams.elementAttributes && typeof editorParams.elementAttributes == "object"){
for (let key in editorParams.elementAttributes){
if(key.charAt(0) == "+"){
key = key.slice(1);
input.setAttribute(key, input.getAttribute(key) + editorParams.elementAttributes["+" + key]);
}else {
input.setAttribute(key, editorParams.elementAttributes[key]);
}
}
}
cellValue = typeof cellValue !== "undefined" ? cellValue : "";
if(inputFormat){
if(DT){
if(DT.isDateTime(cellValue)){
newDatetime = cellValue;
}else if(inputFormat === "iso"){
newDatetime = DT.fromISO(String(cellValue));
}else {
newDatetime = DT.fromFormat(String(cellValue), inputFormat);
}
cellValue = newDatetime.toFormat("yyyy-MM-dd") + "T" + newDatetime.toFormat("hh:mm");
}else {
console.error("Editor Error - 'date' editor 'format' param is dependant on luxon.js");
}
}
input.value = cellValue;
onRendered(function(){
if(cell._getSelf){
input.focus({preventScroll: true});
input.style.height = "100%";
if(editorParams.selectContents){
input.select();
}
}
});
function onChange(){
var value = input.value,
luxDateTime;
if(((cellValue === null || typeof cellValue === "undefined") && value !== "") || value !== cellValue){
if(value && inputFormat){
luxDateTime = DT.fromISO(String(value));
switch(inputFormat){
case true:
value = luxDateTime;
break;
case "iso":
value = luxDateTime.toISO();
break;
default:
value = luxDateTime.toFormat(inputFormat);
}
}
if(success(value)){
cellValue = input.value; //persist value if successfully validated incase editor is used as header filter
}
}else {
cancel();
}
}
//submit new value on blur
input.addEventListener("blur", function(e) {
if (e.relatedTarget || e.rangeParent || e.explicitOriginalTarget !== input) {
onChange(); // only on a "true" blur; not when focusing browser's date/time picker
}
});
//submit new value on enter
input.addEventListener("keydown", function(e){
switch(e.keyCode){
// case 9:
case 13:
onChange();
break;
case 27:
cancel();
break;
case 35:
case 36:
e.stopPropagation();
break;
case 38: //up arrow
case 40: //down arrow
if(vertNav == "editor"){
e.stopImmediatePropagation();
e.stopPropagation();
}
break;
}
});
return input;
}
class Edit{
constructor(editor, cell, onRendered, success, cancel, editorParams){
this.edit = editor;
this.table = editor.table;
this.cell = cell;
this.params = this._initializeParams(editorParams);
this.data = [];
this.displayItems = [];
this.currentItems = [];
this.focusedItem = null;
this.input = this._createInputElement();
this.listEl = this._createListElement();
this.initialValues = null;
this.isFilter = !cell._getSelf;
this.filterTimeout = null;
this.filtered = false;
this.typing = false;
this.values = [];
this.popup = null;
this.listIteration = 0;
this.lastAction="";
this.filterTerm="";
this.blurable = true;
this.actions = {
success:success,
cancel:cancel
};
this._deprecatedOptionsCheck();
this._initializeValue();
onRendered(this._onRendered.bind(this));
}
_deprecatedOptionsCheck(){
if(this.params.listItemFormatter){
this.cell.getTable().deprecationAdvisor.msg("The listItemFormatter editor param has been deprecated, please see the latest editor documentation for updated options");
}
if(this.params.sortValuesList){
this.cell.getTable().deprecationAdvisor.msg("The sortValuesList editor param has been deprecated, please see the latest editor documentation for updated options");
}
if(this.params.searchFunc){
this.cell.getTable().deprecationAdvisor.msg("The searchFunc editor param has been deprecated, please see the latest editor documentation for updated options");
}
if(this.params.searchingPlaceholder){
this.cell.getTable().deprecationAdvisor.msg("The searchingPlaceholder editor param has been deprecated, please see the latest editor documentation for updated options");
}
}
_initializeValue(){
var initialValue = this.cell.getValue();
if(typeof initialValue === "undefined" && typeof this.params.defaultValue !== "undefined"){
initialValue = this.params.defaultValue;
}
this.initialValues = this.params.multiselect ? initialValue : [initialValue];
if(this.isFilter){
this.input.value = this.initialValues ? this.initialValues.join(",") : "";
this.headerFilterInitialListGen();
}
}
_onRendered(){
var cellEl = this.cell.getElement();
function clickStop(e){
e.stopPropagation();
}
if(!this.isFilter){
this.input.style.height = "100%";
this.input.focus({preventScroll: true});
}
cellEl.addEventListener("click", clickStop);
setTimeout(() => {
cellEl.removeEventListener("click", clickStop);
}, 1000);
this.input.addEventListener("mousedown", this._preventPopupBlur.bind(this));
}
_createListElement(){
var listEl = document.createElement("div");
listEl.classList.add("tabulator-edit-list");
listEl.addEventListener("mousedown", this._preventBlur.bind(this));
listEl.addEventListener("keydown", this._inputKeyDown.bind(this));
return listEl;
}
_setListWidth(){
var element = this.isFilter ? this.input : this.cell.getElement();
this.listEl.style.minWidth = element.offsetWidth + "px";
if(this.params.maxWidth){
if(this.params.maxWidth === true){
this.listEl.style.maxWidth = element.offsetWidth + "px";
}else if(typeof this.params.maxWidth === "number"){
this.listEl.style.maxWidth = this.params.maxWidth + "px";
}else {
this.listEl.style.maxWidth = this.params.maxWidth;
}
}
}
_createInputElement(){
var attribs = this.params.elementAttributes;
var input = document.createElement("input");
input.setAttribute("type", this.params.clearable ? "search" : "text");
input.style.padding = "4px";
input.style.width = "100%";
input.style.boxSizing = "border-box";
if(!this.params.autocomplete){
input.style.cursor = "default";
input.style.caretColor = "transparent";
// input.readOnly = (this.edit.currentCell != false);
}
if(attribs && typeof attribs == "object"){
for (let key in attribs){
if(key.charAt(0) == "+"){
key = key.slice(1);
input.setAttribute(key, input.getAttribute(key) + attribs["+" + key]);
}else {
input.setAttribute(key, attribs[key]);
}
}
}
if(this.params.mask){
maskInput(input, this.params);
}
this._bindInputEvents(input);
return input;
}
_initializeParams(params){
var valueKeys = ["values", "valuesURL", "valuesLookup"],
valueCheck;
params = Object.assign({}, params);
params.verticalNavigation = params.verticalNavigation || "editor";
params.placeholderLoading = typeof params.placeholderLoading === "undefined" ? "Searching ..." : params.placeholderLoading;
params.placeholderEmpty = typeof params.placeholderEmpty === "undefined" ? "No Results Found" : params.placeholderEmpty;
params.filterDelay = typeof params.filterDelay === "undefined" ? 300 : params.filterDelay;
params.emptyValue = Object.keys(params).includes("emptyValue") ? params.emptyValue : "";
valueCheck = Object.keys(params).filter(key => valueKeys.includes(key)).length;
if(!valueCheck){
console.warn("list editor config error - either the values, valuesURL, or valuesLookup option must be set");
}else if(valueCheck > 1){
console.warn("list editor config error - only one of the values, valuesURL, or valuesLookup options can be set on the same editor");
}
if(params.autocomplete){
if(params.multiselect){
params.multiselect = false;
console.warn("list editor config error - multiselect option is not available when autocomplete is enabled");
}
}else {
if(params.freetext){
params.freetext = false;
console.warn("list editor config error - freetext option is only available when autocomplete is enabled");
}
if(params.filterFunc){
params.filterFunc = false;
console.warn("list editor config error - filterFunc option is only available when autocomplete is enabled");
}
if(params.filterRemote){
params.filterRemote = false;
console.warn("list editor config error - filterRemote option is only available when autocomplete is enabled");
}
if(params.mask){
params.mask = false;
console.warn("list editor config error - mask option is only available when autocomplete is enabled");
}
if(params.allowEmpty){
params.allowEmpty = false;
console.warn("list editor config error - allowEmpty option is only available when autocomplete is enabled");
}
if(params.listOnEmpty){
params.listOnEmpty = false;
console.warn("list editor config error - listOnEmpty option is only available when autocomplete is enabled");
}
}
if(params.filterRemote && !(typeof params.valuesLookup === "function" || params.valuesURL)){
params.filterRemote = false;
console.warn("list editor config error - filterRemote option should only be used when values list is populated from a remote source");
}
return params;
}
//////////////////////////////////////
////////// Event Handling ////////////
//////////////////////////////////////
_bindInputEvents(input){
input.addEventListener("focus", this._inputFocus.bind(this));
input.addEventListener("click", this._inputClick.bind(this));
input.addEventListener("blur", this._inputBlur.bind(this));
input.addEventListener("keydown", this._inputKeyDown.bind(this));
input.addEventListener("search", this._inputSearch.bind(this));
if(this.params.autocomplete){
input.addEventListener("keyup", this._inputKeyUp.bind(this));
}
}
_inputFocus(e){
this.rebuildOptionsList();
}
_filter(){
if(this.params.filterRemote){
clearTimeout(this.filterTimeout);
this.filterTimeout = setTimeout(() => {
this.rebuildOptionsList();
}, this.params.filterDelay);
}else {
this._filterList();
}
}
_inputClick(e){
e.stopPropagation();
}
_inputBlur(e){
if(this.blurable){
if(this.popup){
this.popup.hide();
}else {
this._resolveValue(true);
}
}
}
_inputSearch(){
this._clearChoices();
}
_inputKeyDown(e){
switch(e.keyCode){
case 38: //up arrow
this._keyUp(e);
break;
case 40: //down arrow
this._keyDown(e);
break;
case 37: //left arrow
case 39: //right arrow
this._keySide(e);
break;
case 13: //enter
this._keyEnter();
break;
case 27: //escape
this._keyEsc();
break;
case 36: //home
case 35: //end
this._keyHomeEnd(e);
break;
case 9: //tab
break;
default:
this._keySelectLetter(e);
}
}
_inputKeyUp(e){
switch(e.keyCode){
case 38: //up arrow
case 37: //left arrow
case 39: //up arrow
case 40: //right arrow
case 13: //enter
case 27: //escape
break;
default:
this._keyAutoCompLetter(e);
}
}
_preventPopupBlur(){
if(this.popup){
this.popup.blockHide();
}
setTimeout(() =>{
if(this.popup){
this.popup.restoreHide();
}
}, 10);
}
_preventBlur(){
this.blurable = false;
setTimeout(() =>{
this.blurable = true;
}, 10);
}
//////////////////////////////////////
//////// Keyboard Navigation /////////
//////////////////////////////////////
_keyUp(e){
var index = this.displayItems.indexOf(this.focusedItem);
if(this.params.verticalNavigation == "editor" || (this.params.verticalNavigation == "hybrid" && index)){
e.stopImmediatePropagation();
e.stopPropagation();
e.preventDefault();
if(index > 0){
this._focusItem(this.displayItems[index - 1]);
}
}
}
_keyDown(e){
var index = this.displayItems.indexOf(this.focusedItem);
if(this.params.verticalNavigation == "editor" || (this.params.verticalNavigation == "hybrid" && index < this.displayItems.length - 1)){
e.stopImmediatePropagation();
e.stopPropagation();
e.preventDefault();
if(index < this.displayItems.length - 1){
if(index == -1){
this._focusItem(this.displayItems[0]);
}else {
this._focusItem(this.displayItems[index + 1]);
}
}
}
}
_keySide(e){
if(!this.params.autocomplete){
e.stopImmediatePropagation();
e.stopPropagation();
e.preventDefault();
}
}
_keyEnter(e){
if(this.params.autocomplete && this.lastAction === "typing"){
this._resolveValue(true);
}else {
if(this.focusedItem){
this._chooseItem(this.focusedItem);
}
}
}
_keyEsc(e){
this._cancel();
}
_keyHomeEnd(e){
if(this.params.autocomplete){
//prevent table navigation while using input element
e.stopImmediatePropagation();
}
}
_keySelectLetter(e){
if(!this.params.autocomplete){
// if(this.edit.currentCell === false){
e.preventDefault();
// }
if(e.keyCode >= 38 && e.keyCode <= 90){
this._scrollToValue(e.keyCode);
}
}
}
_keyAutoCompLetter(e){
this._filter();
this.lastAction = "typing";
this.typing = true;
}
_scrollToValue(char){
clearTimeout(this.filterTimeout);
var character = String.fromCharCode(char).toLowerCase();
this.filterTerm += character.toLowerCase();
var match = this.displayItems.find((item) => {
return typeof item.label !== "undefined" && item.label.toLowerCase().startsWith(this.filterTerm);
});
if(match){
this._focusItem(match);
}
this.filterTimeout = setTimeout(() => {
this.filterTerm = "";
}, 800);
}
_focusItem(item){
this.lastAction = "focus";
if(this.focusedItem && this.focusedItem.element){
this.focusedItem.element.classList.remove("focused");
}
this.focusedItem = item;
if(item && item.element){
item.element.classList.add("focused");
item.element.scrollIntoView({behavior: 'smooth', block: 'nearest', inline: 'start'});
}
}
//////////////////////////////////////
/////// Data List Generation /////////
//////////////////////////////////////
headerFilterInitialListGen(){
this._generateOptions(true);
}
rebuildOptionsList(){
this._generateOptions()
.then(this._sortOptions.bind(this))
.then(this._buildList.bind(this))
.then(this._showList.bind(this))
.catch((e) => {
if(!Number.isInteger(e)){
console.error("List generation error", e);
}
});
}
_filterList(){
this._buildList(this._filterOptions());
this._showList();
}
_generateOptions(silent){
var values = [];
var iteration = ++ this.listIteration;
this.filtered = false;
if(this.params.values){
values = this.params.values;
}else if (this.params.valuesURL){
values = this._ajaxRequest(this.params.valuesURL, this.input.value);
}else {
if(typeof this.params.valuesLookup === "function"){
values = this.params.valuesLookup(this.cell, this.input.value);
}else if(this.params.valuesLookup){
values = this._uniqueColumnValues(this.params.valuesLookupField);
}
}
if(values instanceof Promise){
if(!silent){
this._addPlaceholder(this.params.placeholderLoading);
}
return values.then()
.then((responseValues) => {
if(this.listIteration === iteration){
return this._parseList(responseValues);
}else {
return Promise.reject(iteration);
}
});
}else {
return Promise.resolve(this._parseList(values));
}
}
_addPlaceholder(contents){
var placeholder = document.createElement("div");
if(typeof contents === "function"){
contents = contents(this.cell.getComponent(), this.listEl);
}
if(contents){
this._clearList();
if(contents instanceof HTMLElement){
placeholder = contents;
}else {
placeholder.classList.add("tabulator-edit-list-placeholder");
placeholder.innerHTML = contents;
}
this.listEl.appendChild(placeholder);
this._showList();
}
}
_ajaxRequest(url, term){
var params = this.params.filterRemote ? {term:term} : {};
url = urlBuilder(url, {}, params);
return fetch(url)
.then((response)=>{
if(response.ok) {
return response.json()
.catch((error)=>{
console.warn("List Ajax Load Error - Invalid JSON returned", error);
return Promise.reject(error);
});
}else {
console.error("List Ajax Load Error - Connection Error: " + response.status, response.statusText);
return Promise.reject(response);
}
})
.catch((error)=>{
console.error("List Ajax Load Error - Connection Error: ", error);
return Promise.reject(error);
});
}
_uniqueColumnValues(field){
var output = {},
data = this.table.getData(this.params.valuesLookup),
column;
if(field){
column = this.table.columnManager.getColumnByField(field);
}else {
column = this.cell.getColumn()._getSelf();
}
if(column){
data.forEach((row) => {
var val = column.getFieldValue(row);
if(val !== null && typeof val !== "undefined" && val !== ""){
output[val] = true;
}
});
}else {
console.warn("unable to find matching column to create select lookup list:", field);
output = [];
}
return Object.keys(output);
}
_parseList(inputValues){
var data = [];
if(!Array.isArray(inputValues)){
inputValues = Object.entries(inputValues).map(([key, value]) => {
return {
label:value,
value:key,
};
});
}
inputValues.forEach((value) => {
if(typeof value !== "object"){
value = {
label:value,
value:value,
};
}
this._parseListItem(value, data, 0);
});
if(!this.currentItems.length && this.params.freetext){
this.input.value = this.initialValues;
this.typing = true;
this.lastAction = "typing";
}
this.data = data;
return data;
}
_parseListItem(option, data, level){
var item = {};
if(option.options){
item = this._parseListGroup(option, level + 1);
}else {
item = {
label:option.label,
value:option.value,
itemParams:option.itemParams,
elementAttributes: option.elementAttributes,
element:false,
selected:false,
visible:true,
level:level,
original:option,
};
if(this.initialValues && this.initialValues.indexOf(option.value) > -1){
this._chooseItem(item, true);
}
}
data.push(item);
}
_parseListGroup(option, level){
var item = {
label:option.label,
group:true,
itemParams:option.itemParams,
elementAttributes:option.elementAttributes,
element:false,
visible:true,
level:level,
options:[],
original:option,
};
option.options.forEach((child) => {
this._parseListItem(child, item.options, level);
});
return item;
}
_sortOptions(options){
var sorter;
if(this.params.sort){
sorter = typeof this.params.sort === "function" ? this.params.sort : this._defaultSortFunction.bind(this);
this._sortGroup(sorter, options);
}
return options;
}
_sortGroup(sorter, options){
options.sort((a,b) => {
return sorter(a.label, b.label, a.value, b.value, a.original, b.original);
});
options.forEach((option) => {
if(option.group){
this._sortGroup(sorter, option.options);
}
});
}
_defaultSortFunction(as, bs){
var a, b, a1, b1, i= 0, L, rx = /(\d+)|(\D+)/g, rd = /\d/;
var emptyAlign = 0;
if(this.params.sort === "desc"){
[as, bs] = [bs, as];
}
//handle empty values
if(!as && as!== 0){
emptyAlign = !bs && bs!== 0 ? 0 : -1;
}else if(!bs && bs!== 0){
emptyAlign = 1;
}else {
if(isFinite(as) && isFinite(bs)) return as - bs;
a = String(as).toLowerCase();
b = String(bs).toLowerCase();
if(a === b) return 0;
if(!(rd.test(a) && rd.test(b))) return a > b ? 1 : -1;
a = a.match(rx);
b = b.match(rx);
L = a.length > b.length ? b.length : a.length;
while(i < L){
a1= a[i];
b1= b[i++];
if(a1 !== b1){
if(isFinite(a1) && isFinite(b1)){
if(a1.charAt(0) === "0") a1 = "." + a1;
if(b1.charAt(0) === "0") b1 = "." + b1;
return a1 - b1;
}
else return a1 > b1 ? 1 : -1;
}
}
return a.length > b.length;
}
return emptyAlign;
}
_filterOptions(){
var filterFunc = this.params.filterFunc || this._defaultFilterFunc,
term = this.input.value;
if(term){
this.filtered = true;
this.data.forEach((item) => {
this._filterItem(filterFunc, term, item);
});
}else {
this.filtered = false;
}
return this.data;
}
_filterItem(func, term, item){
var matches = false;
if(!item.group){
item.visible = func(term, item.label, item.value, item.original);
}else {
item.options.forEach((option) => {
if(this._filterItem(func, term, option)){
matches = true;
}
});
item.visible = matches;
}
return item.visible;
}
_defaultFilterFunc(term, label, value, item){
term = String(term).toLowerCase();
if(label !== null && typeof label !== "undefined"){
if(String(label).toLowerCase().indexOf(term) > -1 || String(value).toLowerCase().indexOf(term) > -1){
return true;
}
}
return false;
}
//////////////////////////////////////
/////////// Display List /////////////
//////////////////////////////////////
_clearList(){
while(this.listEl.firstChild) this.listEl.removeChild(this.listEl.firstChild);
this.displayItems = [];
}
_buildList(data){
this._clearList();
data.forEach((option) => {
this._buildItem(option);
});
if(!this.displayItems.length){
this._addPlaceholder(this.params.placeholderEmpty);
}
}
_buildItem(item){
var el = item.element,
contents;
if(!this.filtered || item.visible){
if(!el){
el = document.createElement("div");
el.tabIndex = 0;
contents = this.params.itemFormatter ? this.params.itemFormatter(item.label, item.value, item.original, el) : item.label;
if(contents instanceof HTMLElement){
el.appendChild(contents);
}else {
el.innerHTML = contents;
}
if(item.group){
el.classList.add("tabulator-edit-list-group");
}else {
el.classList.add("tabulator-edit-list-item");
}
el.classList.add("tabulator-edit-list-group-level-" + item.level);
if(item.elementAttributes && typeof item.elementAttributes == "object"){
for (let key in item.elementAttributes){
if(key.charAt(0) == "+"){
key = key.slice(1);
el.setAttribute(key, this.input.getAttribute(key) + item.elementAttributes["+" + key]);
}else {
el.setAttribute(key, item.elementAttributes[key]);
}
}
}
if(item.group){
el.addEventListener("click", this._groupClick.bind(this, item));
}else {
el.addEventListener("click", this._itemClick.bind(this, item));
}
el.addEventListener("mousedown", this._preventBlur.bind(this));
item.element = el;
}
this._styleItem(item);
this.listEl.appendChild(el);
if(item.group){
item.options.forEach((option) => {
this._buildItem(option);
});
}else {
this.displayItems.push(item);
}
}
}
_showList(){
var startVis = this.popup && this.popup.isVisible();
if(this.input.parentNode){
if(this.params.autocomplete && this.input.value === "" && !this.params.listOnEmpty){
if(this.popup){
this.popup.hide(true);
}
return;
}
this._setListWidth();
if(!this.popup){
this.popup = this.edit.popup(this.listEl);
}
this.popup.show(this.cell.getElement(), "bottom");
if(!startVis){
setTimeout(() => {
this.popup.hideOnBlur(this._resolveValue.bind(this, true));
}, 10);
}
}
}
_styleItem(item){
if(item && item.element){
if(item.selected){
item.element.classList.add("active");
}else {
item.element.classList.remove("active");
}
}
}
//////////////////////////////////////
///////// User Interaction ///////////
//////////////////////////////////////
_itemClick(item, e){
e.stopPropagation();
this._chooseItem(item);
}
_groupClick(item, e){
e.stopPropagation();
}
//////////////////////////////////////
////// Current Item Management ///////
//////////////////////////////////////
_cancel(){
this.popup.hide(true);
this.actions.cancel();
}
_clearChoices(){
this.typing = true;
this.currentItems.forEach((item) => {
item.selected = false;
this._styleItem(item);
});
this.currentItems = [];
this.focusedItem = null;
}
_chooseItem(item, silent){
var index;
this.typing = false;
if(this.params.multiselect){
index = this.currentItems.indexOf(item);
if(index > -1){
this.currentItems.splice(index, 1);
item.selected = false;
}else {
this.currentItems.push(item);
item.selected = true;
}
this.input.value = this.currentItems.map(item => item.label).join(",");
this._styleItem(item);
}else {
this.currentItems = [item];
item.selected = true;
this.input.value = item.label;
this._styleItem(item);
if(!silent){
this._resolveValue();
}
}
this._focusItem(item);
}
_resolveValue(blur){
var output, initialValue;
if(this.popup){
this.popup.hide(true);
}
if(this.params.multiselect){
output = this.currentItems.map(item => item.value);
}else {
if(blur && this.params.autocomplete && this.typing){
if(this.params.freetext || (this.params.allowEmpty && this.input.value === "")){
output = this.input.value;
}else {
this.actions.cancel();
return;
}
}else {
if(this.currentItems[0]){
output = this.currentItems[0].value;
}else {
initialValue = Array.isArray(this.initialValues) ? this.initialValues[0] : this.initialValues;
if(initialValue === null || typeof initialValue === "undefined" || initialValue === ""){
output = initialValue;
}else {
output = this.params.emptyValue;
}
}
}
}
if(output === ""){
output = this.params.emptyValue;
}
this.actions.success(output);
if(this.isFilter){
this.initialValues = output && !Array.isArray(output) ? [output] : output;
this.currentItems = [];
}
}
}
function select(cell, onRendered, success, cancel, editorParams){
this.deprecationMsg("The select editor has been deprecated, please use the new list editor");
var list = new Edit(this, cell, onRendered, success, cancel, editorParams);
return list.input;
}
function list(cell, onRendered, success, cancel, editorParams){
var list = new Edit(this, cell, onRendered, success, cancel, editorParams);
return list.input;
}
function autocomplete(cell, onRendered, success, cancel, editorParams){
this.deprecationMsg("The autocomplete editor has been deprecated, please use the new list editor with the 'autocomplete' editorParam");
editorParams.autocomplete = true;
var list = new Edit(this, cell, onRendered, success, cancel, editorParams);
return list.input;
}
//star rating
function star(cell, onRendered, success, cancel, editorParams){
var self = this,
element = cell.getElement(),
value = cell.getValue(),
maxStars = element.getElementsByTagName("svg").length || 5,
size = element.getElementsByTagName("svg")[0] ? element.getElementsByTagName("svg")[0].getAttribute("width") : 14,
stars = [],
starsHolder = document.createElement("div"),
star = document.createElementNS('http://www.w3.org/2000/svg', "svg");
//change star type
function starChange(val){
stars.forEach(function(star, i){
if(i < val){
if(self.table.browser == "ie"){
star.setAttribute("class", "tabulator-star-active");
}else {
star.classList.replace("tabulator-star-inactive", "tabulator-star-active");
}
star.innerHTML = '';
}else {
if(self.table.browser == "ie"){
star.setAttribute("class", "tabulator-star-inactive");
}else {
star.classList.replace("tabulator-star-active", "tabulator-star-inactive");
}
star.innerHTML = '';
}
});
}
//build stars
function buildStar(i){
var starHolder = document.createElement("span");
var nextStar = star.cloneNode(true);
stars.push(nextStar);
starHolder.addEventListener("mouseenter", function(e){
e.stopPropagation();
e.stopImmediatePropagation();
starChange(i);
});
starHolder.addEventListener("mousemove", function(e){
e.stopPropagation();
e.stopImmediatePropagation();
});
starHolder.addEventListener("click", function(e){
e.stopPropagation();
e.stopImmediatePropagation();
success(i);
element.blur();
});
starHolder.appendChild(nextStar);
starsHolder.appendChild(starHolder);
}
//handle keyboard navigation value change
function changeValue(val){
value = val;
starChange(val);
}
//style cell
element.style.whiteSpace = "nowrap";
element.style.overflow = "hidden";
element.style.textOverflow = "ellipsis";
//style holding element
starsHolder.style.verticalAlign = "middle";
starsHolder.style.display = "inline-block";
starsHolder.style.padding = "4px";
//style star
star.setAttribute("width", size);
star.setAttribute("height", size);
star.setAttribute("viewBox", "0 0 512 512");
star.setAttribute("xml:space", "preserve");
star.style.padding = "0 1px";
if(editorParams.elementAttributes && typeof editorParams.elementAttributes == "object"){
for (let key in editorParams.elementAttributes){
if(key.charAt(0) == "+"){
key = key.slice(1);
starsHolder.setAttribute(key, starsHolder.getAttribute(key) + editorParams.elementAttributes["+" + key]);
}else {
starsHolder.setAttribute(key, editorParams.elementAttributes[key]);
}
}
}
//create correct number of stars
for(var i=1;i<= maxStars;i++){
buildStar(i);
}
//ensure value does not exceed number of stars
value = Math.min(parseInt(value), maxStars);
// set initial styling of stars
starChange(value);
starsHolder.addEventListener("mousemove", function(e){
starChange(0);
});
starsHolder.addEventListener("click", function(e){
success(0);
});
element.addEventListener("blur", function(e){
cancel();
});
//allow key based navigation
element.addEventListener("keydown", function(e){
switch(e.keyCode){
case 39: //right arrow
changeValue(value + 1);
break;
case 37: //left arrow
changeValue(value - 1);
break;
case 13: //enter
success(value);
break;
case 27: //escape
cancel();
break;
}
});
return starsHolder;
}
//draggable progress bar
function progress(cell, onRendered, success, cancel, editorParams){
var element = cell.getElement(),
max = typeof editorParams.max === "undefined" ? ((element.getElementsByTagName("div")[0] && element.getElementsByTagName("div")[0].getAttribute("max")) || 100) : editorParams.max,
min = typeof editorParams.min === "undefined" ? ((element.getElementsByTagName("div")[0] && element.getElementsByTagName("div")[0].getAttribute("min")) || 0) : editorParams.min,
percent = (max - min) / 100,
value = cell.getValue() || 0,
handle = document.createElement("div"),
bar = document.createElement("div"),
mouseDrag, mouseDragWidth;
//set new value
function updateValue(){
var style = window.getComputedStyle(element, null);
var calcVal = (percent * Math.round(bar.offsetWidth / ((element.clientWidth - parseInt(style.getPropertyValue("padding-left")) - parseInt(style.getPropertyValue("padding-right")))/100))) + min;
success(calcVal);
element.setAttribute("aria-valuenow", calcVal);
element.setAttribute("aria-label", value);
}
//style handle
handle.style.position = "absolute";
handle.style.right = "0";
handle.style.top = "0";
handle.style.bottom = "0";
handle.style.width = "5px";
handle.classList.add("tabulator-progress-handle");
//style bar
bar.style.display = "inline-block";
bar.style.position = "relative";
// bar.style.top = "8px";
// bar.style.bottom = "8px";
// bar.style.left = "4px";
// bar.style.marginRight = "4px";
bar.style.height = "100%";
bar.style.backgroundColor = "#488CE9";
bar.style.maxWidth = "100%";
bar.style.minWidth = "0%";
if(editorParams.elementAttributes && typeof editorParams.elementAttributes == "object"){
for (let key in editorParams.elementAttributes){
if(key.charAt(0) == "+"){
key = key.slice(1);
bar.setAttribute(key, bar.getAttribute(key) + editorParams.elementAttributes["+" + key]);
}else {
bar.setAttribute(key, editorParams.elementAttributes[key]);
}
}
}
//style cell
element.style.padding = "4px 4px";
//make sure value is in range
value = Math.min(parseFloat(value), max);
value = Math.max(parseFloat(value), min);
//workout percentage
value = Math.round((value - min) / percent);
// bar.style.right = value + "%";
bar.style.width = value + "%";
element.setAttribute("aria-valuemin", min);
element.setAttribute("aria-valuemax", max);
bar.appendChild(handle);
handle.addEventListener("mousedown", function(e){
mouseDrag = e.screenX;
mouseDragWidth = bar.offsetWidth;
});
handle.addEventListener("mouseover", function(){
handle.style.cursor = "ew-resize";
});
element.addEventListener("mousemove", function(e){
if(mouseDrag){
bar.style.width = (mouseDragWidth + e.screenX - mouseDrag) + "px";
}
});
element.addEventListener("mouseup", function(e){
if(mouseDrag){
e.stopPropagation();
e.stopImmediatePropagation();
mouseDrag = false;
mouseDragWidth = false;
updateValue();
}
});
//allow key based navigation
element.addEventListener("keydown", function(e){
switch(e.keyCode){
case 39: //right arrow
e.preventDefault();
bar.style.width = (bar.clientWidth + element.clientWidth/100) + "px";
break;
case 37: //left arrow
e.preventDefault();
bar.style.width = (bar.clientWidth - element.clientWidth/100) + "px";
break;
case 9: //tab
case 13: //enter
updateValue();
break;
case 27: //escape
cancel();
break;
}
});
element.addEventListener("blur", function(){
cancel();
});
return bar;
}
//checkbox
function tickCross(cell, onRendered, success, cancel, editorParams){
var value = cell.getValue(),
input = document.createElement("input"),
tristate = editorParams.tristate,
indetermValue = typeof editorParams.indeterminateValue === "undefined" ? null : editorParams.indeterminateValue,
indetermState = false,
trueValueSet = Object.keys(editorParams).includes("trueValue"),
falseValueSet = Object.keys(editorParams).includes("falseValue");
input.setAttribute("type", "checkbox");
input.style.marginTop = "5px";
input.style.boxSizing = "border-box";
if(editorParams.elementAttributes && typeof editorParams.elementAttributes == "object"){
for (let key in editorParams.elementAttributes){
if(key.charAt(0) == "+"){
key = key.slice(1);
input.setAttribute(key, input.getAttribute(key) + editorParams.elementAttributes["+" + key]);
}else {
input.setAttribute(key, editorParams.elementAttributes[key]);
}
}
}
input.value = value;
if(tristate && (typeof value === "undefined" || value === indetermValue || value === "")){
indetermState = true;
input.indeterminate = true;
}
if(this.table.browser != "firefox" && this.table.browser != "safari"){ //prevent blur issue on mac firefox
onRendered(function(){
if(cell._getSelf){
input.focus({preventScroll: true});
}
});
}
input.checked = trueValueSet ? value === editorParams.trueValue : (value === true || value === "true" || value === "True" || value === 1);
function setValue(blur){
var checkedValue = input.checked;
if(trueValueSet && checkedValue){
checkedValue = editorParams.trueValue;
}else if(falseValueSet && !checkedValue){
checkedValue = editorParams.falseValue;
}
if(tristate){
if(!blur){
if(input.checked && !indetermState){
input.checked = false;
input.indeterminate = true;
indetermState = true;
return indetermValue;
}else {
indetermState = false;
return checkedValue;
}
}else {
if(indetermState){
return indetermValue;
}else {
return checkedValue;
}
}
}else {
return checkedValue;
}
}
//submit new value on blur
input.addEventListener("change", function(e){
success(setValue());
});
input.addEventListener("blur", function(e){
success(setValue(true));
});
//submit new value on enter
input.addEventListener("keydown", function(e){
if(e.keyCode == 13){
success(setValue());
}
if(e.keyCode == 27){
cancel();
}
});
return input;
}
var defaultEditors = {
input:input,
textarea:textarea,
number:number,
range:range,
date:date,
time:time,
datetime:datetime,
select:select,
list:list,
autocomplete:autocomplete,
star:star,
progress:progress,
tickCross:tickCross,
};
class Edit$1 extends Module{
constructor(table){
super(table);
this.currentCell = false; //hold currently editing cell
this.mouseClick = false; //hold mousedown state to prevent click binding being overridden by editor opening
this.recursionBlock = false; //prevent focus recursion
this.invalidEdit = false;
this.editedCells = [];
this.editors = Edit$1.editors;
this.registerColumnOption("editable");
this.registerColumnOption("editor");
this.registerColumnOption("editorParams");
this.registerColumnOption("cellEditing");
this.registerColumnOption("cellEdited");
this.registerColumnOption("cellEditCancelled");
this.registerTableFunction("getEditedCells", this.getEditedCells.bind(this));
this.registerTableFunction("clearCellEdited", this.clearCellEdited.bind(this));
this.registerTableFunction("navigatePrev", this.navigatePrev.bind(this));
this.registerTableFunction("navigateNext", this.navigateNext.bind(this));
this.registerTableFunction("navigateLeft", this.navigateLeft.bind(this));
this.registerTableFunction("navigateRight", this.navigateRight.bind(this));
this.registerTableFunction("navigateUp", this.navigateUp.bind(this));
this.registerTableFunction("navigateDown", this.navigateDown.bind(this));
this.registerComponentFunction("cell", "isEdited", this.cellIsEdited.bind(this));
this.registerComponentFunction("cell", "clearEdited", this.clearEdited.bind(this));
this.registerComponentFunction("cell", "edit", this.editCell.bind(this));
this.registerComponentFunction("cell", "cancelEdit", this.cellCancelEdit.bind(this));
this.registerComponentFunction("cell", "navigatePrev", this.navigatePrev.bind(this));
this.registerComponentFunction("cell", "navigateNext", this.navigateNext.bind(this));
this.registerComponentFunction("cell", "navigateLeft", this.navigateLeft.bind(this));
this.registerComponentFunction("cell", "navigateRight", this.navigateRight.bind(this));
this.registerComponentFunction("cell", "navigateUp", this.navigateUp.bind(this));
this.registerComponentFunction("cell", "navigateDown", this.navigateDown.bind(this));
}
initialize(){
this.subscribe("cell-init", this.bindEditor.bind(this));
this.subscribe("cell-delete", this.clearEdited.bind(this));
this.subscribe("cell-value-changed", this.updateCellClass.bind(this));
this.subscribe("column-layout", this.initializeColumnCheck.bind(this));
this.subscribe("column-delete", this.columnDeleteCheck.bind(this));
this.subscribe("row-deleting", this.rowDeleteCheck.bind(this));
this.subscribe("row-layout", this.rowEditableCheck.bind(this));
this.subscribe("data-refreshing", this.cancelEdit.bind(this));
this.subscribe("keybinding-nav-prev", this.navigatePrev.bind(this, undefined));
this.subscribe("keybinding-nav-next", this.keybindingNavigateNext.bind(this));
this.subscribe("keybinding-nav-left", this.navigateLeft.bind(this, undefined));
this.subscribe("keybinding-nav-right", this.navigateRight.bind(this, undefined));
this.subscribe("keybinding-nav-up", this.navigateUp.bind(this, undefined));
this.subscribe("keybinding-nav-down", this.navigateDown.bind(this, undefined));
}
///////////////////////////////////
////// Keybinding Functions ///////
///////////////////////////////////
keybindingNavigateNext(e){
var cell = this.currentCell,
newRow = this.options("tabEndNewRow");
if(cell){
if(!this.navigateNext(cell, e)){
if(newRow){
cell.getElement().firstChild.blur();
if(newRow === true){
newRow = this.table.addRow({});
}else {
if(typeof newRow == "function"){
newRow = this.table.addRow(newRow(cell.row.getComponent()));
}else {
newRow = this.table.addRow(Object.assign({}, newRow));
}
}
newRow.then(() => {
setTimeout(() => {
cell.getComponent().navigateNext();
});
});
}
}
}
}
///////////////////////////////////
///////// Cell Functions //////////
///////////////////////////////////
cellIsEdited(cell){
return !! cell.modules.edit && cell.modules.edit.edited;
}
cellCancelEdit(cell){
if(cell === this.currentCell){
this.table.modules.edit.cancelEdit();
}else {
console.warn("Cancel Editor Error - This cell is not currently being edited ");
}
}
///////////////////////////////////
///////// Table Functions /////////
///////////////////////////////////
updateCellClass(cell){
if(this.allowEdit(cell)) {
cell.getElement().classList.add("tabulator-editable");
}
else {
cell.getElement().classList.remove("tabulator-editable");
}
}
clearCellEdited(cells){
if(!cells){
cells = this.table.modules.edit.getEditedCells();
}
if(!Array.isArray(cells)){
cells = [cells];
}
cells.forEach((cell) => {
this.table.modules.edit.clearEdited(cell._getSelf());
});
}
navigatePrev(cell = this.currentCell, e){
var nextCell, prevRow;
if(cell){
if(e){
e.preventDefault();
}
nextCell = this.navigateLeft();
if(nextCell){
return true;
}else {
prevRow = this.table.rowManager.prevDisplayRow(cell.row, true);
if(prevRow){
nextCell = this.findPrevEditableCell(prevRow, prevRow.cells.length);
if(nextCell){
nextCell.getComponent().edit();
return true;
}
}
}
}
return false;
}
navigateNext(cell = this.currentCell, e){
var nextCell, nextRow;
if(cell){
if(e){
e.preventDefault();
}
nextCell = this.navigateRight();
if(nextCell){
return true;
}else {
nextRow = this.table.rowManager.nextDisplayRow(cell.row, true);
if(nextRow){
nextCell = this.findNextEditableCell(nextRow, -1);
if(nextCell){
nextCell.getComponent().edit();
return true;
}
}
}
}
return false;
}
navigateLeft(cell = this.currentCell, e){
var index, nextCell;
if(cell){
if(e){
e.preventDefault();
}
index = cell.getIndex();
nextCell = this.findPrevEditableCell(cell.row, index);
if(nextCell){
nextCell.getComponent().edit();
return true;
}
}
return false;
}
navigateRight(cell = this.currentCell, e){
var index, nextCell;
if(cell){
if(e){
e.preventDefault();
}
index = cell.getIndex();
nextCell = this.findNextEditableCell(cell.row, index);
if(nextCell){
nextCell.getComponent().edit();
return true;
}
}
return false;
}
navigateUp(cell = this.currentCell, e){
var index, nextRow;
if(cell){
if(e){
e.preventDefault();
}
index = cell.getIndex();
nextRow = this.table.rowManager.prevDisplayRow(cell.row, true);
if(nextRow){
nextRow.cells[index].getComponent().edit();
return true;
}
}
return false;
}
navigateDown(cell = this.currentCell, e){
var index, nextRow;
if(cell){
if(e){
e.preventDefault();
}
index = cell.getIndex();
nextRow = this.table.rowManager.nextDisplayRow(cell.row, true);
if(nextRow){
nextRow.cells[index].getComponent().edit();
return true;
}
}
return false;
}
findNextEditableCell(row, index){
var nextCell = false;
if(index < row.cells.length-1){
for(var i = index+1; i < row.cells.length; i++){
let cell = row.cells[i];
if(cell.column.modules.edit && Helpers.elVisible(cell.getElement())){
let allowEdit = this.allowEdit(cell);
if(allowEdit){
nextCell = cell;
break;
}
}
}
}
return nextCell;
}
findPrevEditableCell(row, index){
var prevCell = false;
if(index > 0){
for(var i = index-1; i >= 0; i--){
let cell = row.cells[i];
if(cell.column.modules.edit && Helpers.elVisible(cell.getElement())){
let allowEdit = this.allowEdit(cell);
if(allowEdit){
prevCell = cell;
break;
}
}
}
}
return prevCell;
}
///////////////////////////////////
///////// Internal Logic //////////
///////////////////////////////////
initializeColumnCheck(column){
if(typeof column.definition.editor !== "undefined"){
this.initializeColumn(column);
}
}
columnDeleteCheck(column){
if(this.currentCell && this.currentCell.column === column){
this.cancelEdit();
}
}
rowDeleteCheck(row){
if(this.currentCell && this.currentCell.row === row){
this.cancelEdit();
}
}
rowEditableCheck(row){
row.getCells().forEach((cell) => {
if(cell.column.modules.edit && typeof cell.column.modules.edit.check === "function"){
this.updateCellClass(cell);
}
});
}
//initialize column editor
initializeColumn(column){
var config = {
editor:false,
blocked:false,
check:column.definition.editable,
params:column.definition.editorParams || {}
};
//set column editor
switch(typeof column.definition.editor){
case "string":
if(this.editors[column.definition.editor]){
config.editor = this.editors[column.definition.editor];
}else {
console.warn("Editor Error - No such editor found: ", column.definition.editor);
}
break;
case "function":
config.editor = column.definition.editor;
break;
case "boolean":
if(column.definition.editor === true){
if(typeof column.definition.formatter !== "function"){
if(this.editors[column.definition.formatter]){
config.editor = this.editors[column.definition.formatter];
}else {
config.editor = this.editors["input"];
}
}else {
console.warn("Editor Error - Cannot auto lookup editor for a custom formatter: ", column.definition.formatter);
}
}
break;
}
if(config.editor){
column.modules.edit = config;
}
}
getCurrentCell(){
return this.currentCell ? this.currentCell.getComponent() : false;
}
clearEditor(cancel){
var cell = this.currentCell,
cellEl;
this.invalidEdit = false;
if(cell){
this.currentCell = false;
cellEl = cell.getElement();
this.dispatch("edit-editor-clear", cell, cancel);
cellEl.classList.remove("tabulator-editing");
while(cellEl.firstChild) cellEl.removeChild(cellEl.firstChild);
cell.row.getElement().classList.remove("tabulator-editing");
cell.table.element.classList.remove("tabulator-editing");
}
}
cancelEdit(){
if(this.currentCell){
var cell = this.currentCell;
var component = this.currentCell.getComponent();
this.clearEditor(true);
cell.setValueActual(cell.getValue());
cell.cellRendered();
if(cell.column.definition.editor == "textarea" || cell.column.definition.variableHeight){
cell.row.normalizeHeight(true);
}
if(cell.column.definition.cellEditCancelled){
cell.column.definition.cellEditCancelled.call(this.table, component);
}
this.dispatch("edit-cancelled", cell);
this.dispatchExternal("cellEditCancelled", component);
}
}
//return a formatted value for a cell
bindEditor(cell){
if(cell.column.modules.edit){
var self = this,
element = cell.getElement(true);
this.updateCellClass(cell);
element.setAttribute("tabindex", 0);
element.addEventListener("click", function(e){
if(!element.classList.contains("tabulator-editing")){
element.focus({preventScroll: true});
}
});
element.addEventListener("mousedown", function(e){
if (e.button === 2) {
e.preventDefault();
}else {
self.mouseClick = true;
}
});
element.addEventListener("focus", function(e){
if(!self.recursionBlock){
self.edit(cell, e, false);
}
});
}
}
focusCellNoEvent(cell, block){
this.recursionBlock = true;
if(!(block && this.table.browser === "ie")){
cell.getElement().focus({preventScroll: true});
}
this.recursionBlock = false;
}
editCell(cell, forceEdit){
this.focusCellNoEvent(cell);
this.edit(cell, false, forceEdit);
}
focusScrollAdjust(cell){
if(this.table.rowManager.getRenderMode() == "virtual"){
var topEdge = this.table.rowManager.element.scrollTop,
bottomEdge = this.table.rowManager.element.clientHeight + this.table.rowManager.element.scrollTop,
rowEl = cell.row.getElement();
if(rowEl.offsetTop < topEdge){
this.table.rowManager.element.scrollTop -= (topEdge - rowEl.offsetTop);
}else {
if(rowEl.offsetTop + rowEl.offsetHeight > bottomEdge){
this.table.rowManager.element.scrollTop += (rowEl.offsetTop + rowEl.offsetHeight - bottomEdge);
}
}
var leftEdge = this.table.rowManager.element.scrollLeft,
rightEdge = this.table.rowManager.element.clientWidth + this.table.rowManager.element.scrollLeft,
cellEl = cell.getElement();
if(this.table.modExists("frozenColumns")){
leftEdge += parseInt(this.table.modules.frozenColumns.leftMargin);
rightEdge -= parseInt(this.table.modules.frozenColumns.rightMargin);
}
if(this.table.options.renderHorizontal === "virtual"){
leftEdge -= parseInt(this.table.columnManager.renderer.vDomPadLeft);
rightEdge -= parseInt(this.table.columnManager.renderer.vDomPadLeft);
}
if(cellEl.offsetLeft < leftEdge){
this.table.rowManager.element.scrollLeft -= (leftEdge - cellEl.offsetLeft);
}else {
if(cellEl.offsetLeft + cellEl.offsetWidth > rightEdge){
this.table.rowManager.element.scrollLeft += (cellEl.offsetLeft + cellEl.offsetWidth - rightEdge);
}
}
}
}
allowEdit(cell) {
var check = cell.column.modules.edit ? true : false;
if(cell.column.modules.edit){
switch(typeof cell.column.modules.edit.check){
case "function":
if(cell.row.initialized){
check = cell.column.modules.edit.check(cell.getComponent());
}
break;
case "string":
check = !!cell.row.data[cell.column.modules.edit.check];
break;
case "boolean":
check = cell.column.modules.edit.check;
break;
}
}
return check;
}
edit(cell, e, forceEdit){
var self = this,
allowEdit = true,
rendered = function(){},
element = cell.getElement(),
cellEditor, component, params;
//prevent editing if another cell is refusing to leave focus (eg. validation fail)
if(this.currentCell){
if(!this.invalidEdit && this.currentCell !== cell){
this.cancelEdit();
}
return;
}
//handle successful value change
function success(value){
if(self.currentCell === cell){
var valid = self.chain("edit-success", [cell, value], true, true);
if(valid === true || self.table.options.validationMode === "highlight"){
self.clearEditor();
if(!cell.modules.edit){
cell.modules.edit = {};
}
cell.modules.edit.edited = true;
if(self.editedCells.indexOf(cell) == -1){
self.editedCells.push(cell);
}
cell.setValue(value, true);
return valid === true;
}else {
self.invalidEdit = true;
self.focusCellNoEvent(cell, true);
rendered();
return false;
}
}
}
//handle aborted edit
function cancel(){
if(self.currentCell === cell){
self.cancelEdit();
}
}
function onRendered(callback){
rendered = callback;
}
if(!cell.column.modules.edit.blocked){
if(e){
e.stopPropagation();
}
allowEdit = this.allowEdit(cell);
if(allowEdit || forceEdit){
self.cancelEdit();
self.currentCell = cell;
this.focusScrollAdjust(cell);
component = cell.getComponent();
if(this.mouseClick){
this.mouseClick = false;
if(cell.column.definition.cellClick){
cell.column.definition.cellClick.call(this.table, e, component);
}
}
if(cell.column.definition.cellEditing){
cell.column.definition.cellEditing.call(this.table, component);
}
this.dispatch("cell-editing", cell);
this.dispatchExternal("cellEditing", component);
params = typeof cell.column.modules.edit.params === "function" ? cell.column.modules.edit.params(component) : cell.column.modules.edit.params;
cellEditor = cell.column.modules.edit.editor.call(self, component, onRendered, success, cancel, params);
//if editor returned, add to DOM, if false, abort edit
if(this.currentCell && cellEditor !== false){
if(cellEditor instanceof Node){
element.classList.add("tabulator-editing");
cell.row.getElement().classList.add("tabulator-editing");
cell.table.element.classList.add("tabulator-editing");
while(element.firstChild) element.removeChild(element.firstChild);
element.appendChild(cellEditor);
//trigger onRendered Callback
rendered();
//prevent editing from triggering rowClick event
var children = element.children;
for (var i = 0; i < children.length; i++) {
children[i].addEventListener("click", function(e){
e.stopPropagation();
});
}
}else {
console.warn("Edit Error - Editor should return an instance of Node, the editor returned:", cellEditor);
element.blur();
return false;
}
}else {
element.blur();
return false;
}
return true;
}else {
this.mouseClick = false;
element.blur();
return false;
}
}else {
this.mouseClick = false;
element.blur();
return false;
}
}
getEditedCells(){
var output = [];
this.editedCells.forEach((cell) => {
output.push(cell.getComponent());
});
return output;
}
clearEdited(cell){
var editIndex;
if(cell.modules.edit && cell.modules.edit.edited){
cell.modules.edit.edited = false;
this.dispatch("edit-edited-clear", cell);
}
editIndex = this.editedCells.indexOf(cell);
if(editIndex > -1){
this.editedCells.splice(editIndex, 1);
}
}
}
Edit$1.moduleName = "edit";
//load defaults
Edit$1.editors = defaultEditors;
class ExportRow{
constructor(type, columns, component, indent){
this.type = type;
this.columns = columns;
this.component = component || false;
this.indent = indent || 0;
}
}
class ExportColumn{
constructor(value, component, width, height, depth){
this.value = value;
this.component = component || false;
this.width = width;
this.height = height;
this.depth = depth;
}
}
class Export extends Module{
constructor(table){
super(table);
this.config = {};
this.cloneTableStyle = true;
this.colVisProp = "";
this.registerTableOption("htmlOutputConfig", false); //html output config
this.registerColumnOption("htmlOutput");
this.registerColumnOption("titleHtmlOutput");
}
initialize(){
this.registerTableFunction("getHtml", this.getHtml.bind(this));
}
///////////////////////////////////
///////// Table Functions /////////
///////////////////////////////////
///////////////////////////////////
///////// Internal Logic //////////
///////////////////////////////////
generateExportList(config, style, range, colVisProp){
this.cloneTableStyle = style;
this.config = config || {};
this.colVisProp = colVisProp;
var headers = this.config.columnHeaders !== false ? this.headersToExportRows(this.generateColumnGroupHeaders()) : [];
var body = this.bodyToExportRows(this.rowLookup(range));
return headers.concat(body);
}
generateTable(config, style, range, colVisProp){
var list = this.generateExportList(config, style, range, colVisProp);
return this.generateTableElement(list);
}
rowLookup(range){
var rows = [];
if(typeof range == "function"){
range.call(this.table).forEach((row) =>{
row = this.table.rowManager.findRow(row);
if(row){
rows.push(row);
}
});
}else {
switch(range){
case true:
case "visible":
rows = this.table.rowManager.getVisibleRows(false, true);
break;
case "all":
rows = this.table.rowManager.rows;
break;
case "selected":
rows = this.table.modules.selectRow.selectedRows;
break;
case "active":
default:
if(this.table.options.pagination){
rows = this.table.rowManager.getDisplayRows(this.table.rowManager.displayRows.length - 2);
}else {
rows = this.table.rowManager.getDisplayRows();
}
}
}
return Object.assign([], rows);
}
generateColumnGroupHeaders(){
var output = [];
var columns = this.config.columnGroups !== false ? this.table.columnManager.columns : this.table.columnManager.columnsByIndex;
columns.forEach((column) => {
var colData = this.processColumnGroup(column);
if(colData){
output.push(colData);
}
});
return output;
}
processColumnGroup(column){
var subGroups = column.columns,
maxDepth = 0,
title = column.definition["title" + (this.colVisProp.charAt(0).toUpperCase() + this.colVisProp.slice(1))] || column.definition.title;
var groupData = {
title:title,
column:column,
depth:1,
};
if(subGroups.length){
groupData.subGroups = [];
groupData.width = 0;
subGroups.forEach((subGroup) => {
var subGroupData = this.processColumnGroup(subGroup);
if(subGroupData){
groupData.width += subGroupData.width;
groupData.subGroups.push(subGroupData);
if(subGroupData.depth > maxDepth){
maxDepth = subGroupData.depth;
}
}
});
groupData.depth += maxDepth;
if(!groupData.width){
return false;
}
}else {
if(this.columnVisCheck(column)){
groupData.width = 1;
}else {
return false;
}
}
return groupData;
}
columnVisCheck(column){
var visProp = column.definition[this.colVisProp];
if(typeof visProp === "function"){
visProp = visProp.call(this.table, column.getComponent());
}
return visProp !== false && (column.visible || (!column.visible && visProp));
}
headersToExportRows(columns){
var headers = [],
headerDepth = 0,
exportRows = [];
function parseColumnGroup(column, level){
var depth = headerDepth - level;
if(typeof headers[level] === "undefined"){
headers[level] = [];
}
column.height = column.subGroups ? 1 : (depth - column.depth) + 1;
headers[level].push(column);
if(column.height > 1){
for(let i = 1; i < column.height; i ++){
if(typeof headers[level + i] === "undefined"){
headers[level + i] = [];
}
headers[level + i].push(false);
}
}
if(column.width > 1){
for(let i = 1; i < column.width; i ++){
headers[level].push(false);
}
}
if(column.subGroups){
column.subGroups.forEach(function(subGroup){
parseColumnGroup(subGroup, level+1);
});
}
}
//calculate maximum header depth
columns.forEach(function(column){
if(column.depth > headerDepth){
headerDepth = column.depth;
}
});
columns.forEach(function(column){
parseColumnGroup(column,0);
});
headers.forEach((header) => {
var columns = [];
header.forEach((col) => {
if(col){
let title = typeof col.title === "undefined" ? "" : col.title;
columns.push(new ExportColumn(title, col.column.getComponent(), col.width, col.height, col.depth));
}else {
columns.push(null);
}
});
exportRows.push(new ExportRow("header", columns));
});
return exportRows;
}
bodyToExportRows(rows){
var columns = [];
var exportRows = [];
this.table.columnManager.columnsByIndex.forEach((column) => {
if (this.columnVisCheck(column)) {
columns.push(column.getComponent());
}
});
if(this.config.columnCalcs !== false && this.table.modExists("columnCalcs")){
if(this.table.modules.columnCalcs.topInitialized){
rows.unshift(this.table.modules.columnCalcs.topRow);
}
if(this.table.modules.columnCalcs.botInitialized){
rows.push(this.table.modules.columnCalcs.botRow);
}
}
rows = rows.filter((row) => {
switch(row.type){
case "group":
return this.config.rowGroups !== false;
case "calc":
return this.config.columnCalcs !== false;
case "row":
return !(this.table.options.dataTree && this.config.dataTree === false && row.modules.dataTree.parent);
}
return true;
});
rows.forEach((row, i) => {
var rowData = row.getData(this.colVisProp);
var exportCols = [];
var indent = 0;
switch(row.type){
case "group":
indent = row.level;
exportCols.push(new ExportColumn(row.key, row.getComponent(), columns.length, 1));
break;
case "calc" :
case "row" :
columns.forEach((col) => {
exportCols.push(new ExportColumn(col._column.getFieldValue(rowData), col, 1, 1));
});
if(this.table.options.dataTree && this.config.dataTree !== false){
indent = row.modules.dataTree.index;
}
break;
}
exportRows.push(new ExportRow(row.type, exportCols, row.getComponent(), indent));
});
return exportRows;
}
generateTableElement(list){
var table = document.createElement("table"),
headerEl = document.createElement("thead"),
bodyEl = document.createElement("tbody"),
styles = this.lookupTableStyles(),
rowFormatter = this.table.options["rowFormatter" + (this.colVisProp.charAt(0).toUpperCase() + this.colVisProp.slice(1))],
setup = {};
setup.rowFormatter = rowFormatter !== null ? rowFormatter : this.table.options.rowFormatter;
if(this.table.options.dataTree &&this.config.dataTree !== false && this.table.modExists("columnCalcs")){
setup.treeElementField = this.table.modules.dataTree.elementField;
}
//assign group header formatter
setup.groupHeader = this.table.options["groupHeader" + (this.colVisProp.charAt(0).toUpperCase() + this.colVisProp.slice(1))];
if(setup.groupHeader && !Array.isArray(setup.groupHeader)){
setup.groupHeader = [setup.groupHeader];
}
table.classList.add("tabulator-print-table");
this.mapElementStyles(this.table.columnManager.getHeadersElement(), headerEl, ["border-top", "border-left", "border-right", "border-bottom", "background-color", "color", "font-weight", "font-family", "font-size"]);
if(list.length > 1000){
console.warn("It may take a long time to render an HTML table with more than 1000 rows");
}
list.forEach((row, i) => {
let rowEl;
switch(row.type){
case "header":
headerEl.appendChild(this.generateHeaderElement(row, setup, styles));
break;
case "group":
bodyEl.appendChild(this.generateGroupElement(row, setup, styles));
break;
case "calc":
bodyEl.appendChild(this.generateCalcElement(row, setup, styles));
break;
case "row":
rowEl = this.generateRowElement(row, setup, styles);
this.mapElementStyles(((i % 2) && styles.evenRow) ? styles.evenRow : styles.oddRow, rowEl, ["border-top", "border-left", "border-right", "border-bottom", "color", "font-weight", "font-family", "font-size", "background-color"]);
bodyEl.appendChild(rowEl);
break;
}
});
if(headerEl.innerHTML){
table.appendChild(headerEl);
}
table.appendChild(bodyEl);
this.mapElementStyles(this.table.element, table, ["border-top", "border-left", "border-right", "border-bottom"]);
return table;
}
lookupTableStyles(){
var styles = {};
//lookup row styles
if(this.cloneTableStyle && window.getComputedStyle){
styles.oddRow = this.table.element.querySelector(".tabulator-row-odd:not(.tabulator-group):not(.tabulator-calcs)");
styles.evenRow = this.table.element.querySelector(".tabulator-row-even:not(.tabulator-group):not(.tabulator-calcs)");
styles.calcRow = this.table.element.querySelector(".tabulator-row.tabulator-calcs");
styles.firstRow = this.table.element.querySelector(".tabulator-row:not(.tabulator-group):not(.tabulator-calcs)");
styles.firstGroup = this.table.element.getElementsByClassName("tabulator-group")[0];
if(styles.firstRow){
styles.styleCells = styles.firstRow.getElementsByClassName("tabulator-cell");
styles.firstCell = styles.styleCells[0];
styles.lastCell = styles.styleCells[styles.styleCells.length - 1];
}
}
return styles;
}
generateHeaderElement(row, setup, styles){
var rowEl = document.createElement("tr");
row.columns.forEach((column) => {
if(column){
var cellEl = document.createElement("th");
var classNames = column.component._column.definition.cssClass ? column.component._column.definition.cssClass.split(" ") : [];
cellEl.colSpan = column.width;
cellEl.rowSpan = column.height;
cellEl.innerHTML = column.value;
if(this.cloneTableStyle){
cellEl.style.boxSizing = "border-box";
}
classNames.forEach(function(className) {
cellEl.classList.add(className);
});
this.mapElementStyles(column.component.getElement(), cellEl, ["text-align", "border-top", "border-left", "border-right", "border-bottom", "background-color", "color", "font-weight", "font-family", "font-size"]);
this.mapElementStyles(column.component._column.contentElement, cellEl, ["padding-top", "padding-left", "padding-right", "padding-bottom"]);
if(column.component._column.visible){
this.mapElementStyles(column.component.getElement(), cellEl, ["width"]);
}else {
if(column.component._column.definition.width){
cellEl.style.width = column.component._column.definition.width + "px";
}
}
if(column.component._column.parent){
this.mapElementStyles(column.component._column.parent.groupElement, cellEl, ["border-top"]);
}
rowEl.appendChild(cellEl);
}
});
return rowEl;
}
generateGroupElement(row, setup, styles){
var rowEl = document.createElement("tr"),
cellEl = document.createElement("td"),
group = row.columns[0];
rowEl.classList.add("tabulator-print-table-row");
if(setup.groupHeader && setup.groupHeader[row.indent]){
group.value = setup.groupHeader[row.indent](group.value, row.component._group.getRowCount(), row.component._group.getData(), row.component);
}else {
if(setup.groupHeader !== false){
group.value = row.component._group.generator(group.value, row.component._group.getRowCount(), row.component._group.getData(), row.component);
}
}
cellEl.colSpan = group.width;
cellEl.innerHTML = group.value;
rowEl.classList.add("tabulator-print-table-group");
rowEl.classList.add("tabulator-group-level-" + row.indent);
if(group.component.isVisible()){
rowEl.classList.add("tabulator-group-visible");
}
this.mapElementStyles(styles.firstGroup, rowEl, ["border-top", "border-left", "border-right", "border-bottom", "color", "font-weight", "font-family", "font-size", "background-color"]);
this.mapElementStyles(styles.firstGroup, cellEl, ["padding-top", "padding-left", "padding-right", "padding-bottom"]);
rowEl.appendChild(cellEl);
return rowEl;
}
generateCalcElement(row, setup, styles){
var rowEl = this.generateRowElement(row, setup, styles);
rowEl.classList.add("tabulator-print-table-calcs");
this.mapElementStyles(styles.calcRow, rowEl, ["border-top", "border-left", "border-right", "border-bottom", "color", "font-weight", "font-family", "font-size", "background-color"]);
return rowEl;
}
generateRowElement(row, setup, styles){
var rowEl = document.createElement("tr");
rowEl.classList.add("tabulator-print-table-row");
row.columns.forEach((col, i) => {
if(col){
var cellEl = document.createElement("td"),
column = col.component._column,
index = this.table.columnManager.findColumnIndex(column),
value = col.value,
cellStyle;
var cellWrapper = {
modules:{},
getValue:function(){
return value;
},
getField:function(){
return column.definition.field;
},
getElement:function(){
return cellEl;
},
getColumn:function(){
return column.getComponent();
},
getData:function(){
return row.component.getData();
},
getRow:function(){
return row.component;
},
getComponent:function(){
return cellWrapper;
},
column:column,
};
var classNames = column.definition.cssClass ? column.definition.cssClass.split(" ") : [];
classNames.forEach(function(className) {
cellEl.classList.add(className);
});
if(this.table.modExists("format") && this.config.formatCells !== false){
value = this.table.modules.format.formatExportValue(cellWrapper, this.colVisProp);
}else {
switch(typeof value){
case "object":
value = value !== null ? JSON.stringify(value) : "";
break;
case "undefined":
value = "";
break;
}
}
if(value instanceof Node){
cellEl.appendChild(value);
}else {
cellEl.innerHTML = value;
}
cellStyle = styles.styleCells && styles.styleCells[index] ? styles.styleCells[index] : styles.firstCell;
if(cellStyle){
this.mapElementStyles(cellStyle, cellEl, ["padding-top", "padding-left", "padding-right", "padding-bottom", "border-top", "border-left", "border-right", "border-bottom", "color", "font-weight", "font-family", "font-size", "text-align"]);
if(column.definition.align){
cellEl.style.textAlign = column.definition.align;
}
}
if(this.table.options.dataTree && this.config.dataTree !== false){
if((setup.treeElementField && setup.treeElementField == column.field) || (!setup.treeElementField && i == 0)){
if(row.component._row.modules.dataTree.controlEl){
cellEl.insertBefore(row.component._row.modules.dataTree.controlEl.cloneNode(true), cellEl.firstChild);
}
if(row.component._row.modules.dataTree.branchEl){
cellEl.insertBefore(row.component._row.modules.dataTree.branchEl.cloneNode(true), cellEl.firstChild);
}
}
}
rowEl.appendChild(cellEl);
if(cellWrapper.modules.format && cellWrapper.modules.format.renderedCallback){
cellWrapper.modules.format.renderedCallback();
}
}
});
if(setup.rowFormatter && row.type === "row" && this.config.formatCells !== false){
let formatComponent = Object.assign(row.component);
formatComponent.getElement = function(){return rowEl;};
setup.rowFormatter(row.component);
}
return rowEl;
}
generateHTMLTable(list){
var holder = document.createElement("div");
holder.appendChild(this.generateTableElement(list));
return holder.innerHTML;
}
getHtml(visible, style, config, colVisProp){
var list = this.generateExportList(config || this.table.options.htmlOutputConfig, style, visible, colVisProp || "htmlOutput");
return this.generateHTMLTable(list);
}
mapElementStyles(from, to, props){
if(this.cloneTableStyle && from && to){
var lookup = {
"background-color" : "backgroundColor",
"color" : "fontColor",
"width" : "width",
"font-weight" : "fontWeight",
"font-family" : "fontFamily",
"font-size" : "fontSize",
"text-align" : "textAlign",
"border-top" : "borderTop",
"border-left" : "borderLeft",
"border-right" : "borderRight",
"border-bottom" : "borderBottom",
"padding-top" : "paddingTop",
"padding-left" : "paddingLeft",
"padding-right" : "paddingRight",
"padding-bottom" : "paddingBottom",
};
if(window.getComputedStyle){
var fromStyle = window.getComputedStyle(from);
props.forEach(function(prop){
if(!to.style[lookup[prop]]){
to.style[lookup[prop]] = fromStyle.getPropertyValue(prop);
}
});
}
}
}
}
Export.moduleName = "export";
var defaultFilters = {
//equal to
"=":function(filterVal, rowVal, rowData, filterParams){
return rowVal == filterVal ? true : false;
},
//less than
"<":function(filterVal, rowVal, rowData, filterParams){
return rowVal < filterVal ? true : false;
},
//less than or equal to
"<=":function(filterVal, rowVal, rowData, filterParams){
return rowVal <= filterVal ? true : false;
},
//greater than
">":function(filterVal, rowVal, rowData, filterParams){
return rowVal > filterVal ? true : false;
},
//greater than or equal to
">=":function(filterVal, rowVal, rowData, filterParams){
return rowVal >= filterVal ? true : false;
},
//not equal to
"!=":function(filterVal, rowVal, rowData, filterParams){
return rowVal != filterVal ? true : false;
},
"regex":function(filterVal, rowVal, rowData, filterParams){
if(typeof filterVal == "string"){
filterVal = new RegExp(filterVal);
}
return filterVal.test(rowVal);
},
//contains the string
"like":function(filterVal, rowVal, rowData, filterParams){
if(filterVal === null || typeof filterVal === "undefined"){
return rowVal === filterVal ? true : false;
}else {
if(typeof rowVal !== 'undefined' && rowVal !== null){
return String(rowVal).toLowerCase().indexOf(filterVal.toLowerCase()) > -1;
}
else {
return false;
}
}
},
//contains the keywords
"keywords":function(filterVal, rowVal, rowData, filterParams){
var keywords = filterVal.toLowerCase().split(typeof filterParams.separator === "undefined" ? " " : filterParams.separator),
value = String(rowVal === null || typeof rowVal === "undefined" ? "" : rowVal).toLowerCase(),
matches = [];
keywords.forEach((keyword) =>{
if(value.includes(keyword)){
matches.push(true);
}
});
return filterParams.matchAll ? matches.length === keywords.length : !!matches.length;
},
//starts with the string
"starts":function(filterVal, rowVal, rowData, filterParams){
if(filterVal === null || typeof filterVal === "undefined"){
return rowVal === filterVal ? true : false;
}else {
if(typeof rowVal !== 'undefined' && rowVal !== null){
return String(rowVal).toLowerCase().startsWith(filterVal.toLowerCase());
}
else {
return false;
}
}
},
//ends with the string
"ends":function(filterVal, rowVal, rowData, filterParams){
if(filterVal === null || typeof filterVal === "undefined"){
return rowVal === filterVal ? true : false;
}else {
if(typeof rowVal !== 'undefined' && rowVal !== null){
return String(rowVal).toLowerCase().endsWith(filterVal.toLowerCase());
}
else {
return false;
}
}
},
//in array
"in":function(filterVal, rowVal, rowData, filterParams){
if(Array.isArray(filterVal)){
return filterVal.length ? filterVal.indexOf(rowVal) > -1 : true;
}else {
console.warn("Filter Error - filter value is not an array:", filterVal);
return false;
}
},
};
class Filter extends Module{
constructor(table){
super(table);
this.filterList = []; //hold filter list
this.headerFilters = {}; //hold column filters
this.headerFilterColumns = []; //hold columns that use header filters
this.prevHeaderFilterChangeCheck = "";
this.prevHeaderFilterChangeCheck = "{}";
this.changed = false; //has filtering changed since last render
this.tableInitialized = false;
this.registerTableOption("filterMode", "local"); //local or remote filtering
this.registerTableOption("initialFilter", false); //initial filtering criteria
this.registerTableOption("initialHeaderFilter", false); //initial header filtering criteria
this.registerTableOption("headerFilterLiveFilterDelay", 300); //delay before updating column after user types in header filter
this.registerColumnOption("headerFilter");
this.registerColumnOption("headerFilterPlaceholder");
this.registerColumnOption("headerFilterParams");
this.registerColumnOption("headerFilterEmptyCheck");
this.registerColumnOption("headerFilterFunc");
this.registerColumnOption("headerFilterFuncParams");
this.registerColumnOption("headerFilterLiveFilter");
this.registerTableFunction("searchRows", this.searchRows.bind(this));
this.registerTableFunction("searchData", this.searchData.bind(this));
this.registerTableFunction("setFilter", this.userSetFilter.bind(this));
this.registerTableFunction("refreshFilter", this.userRefreshFilter.bind(this));
this.registerTableFunction("addFilter", this.userAddFilter.bind(this));
this.registerTableFunction("getFilters", this.getFilters.bind(this));
this.registerTableFunction("setHeaderFilterFocus", this.userSetHeaderFilterFocus.bind(this));
this.registerTableFunction("getHeaderFilterValue", this.userGetHeaderFilterValue.bind(this));
this.registerTableFunction("setHeaderFilterValue", this.userSetHeaderFilterValue.bind(this));
this.registerTableFunction("getHeaderFilters", this.getHeaderFilters.bind(this));
this.registerTableFunction("removeFilter", this.userRemoveFilter.bind(this));
this.registerTableFunction("clearFilter", this.userClearFilter.bind(this));
this.registerTableFunction("clearHeaderFilter", this.userClearHeaderFilter.bind(this));
this.registerComponentFunction("column", "headerFilterFocus", this.setHeaderFilterFocus.bind(this));
this.registerComponentFunction("column", "reloadHeaderFilter", this.reloadHeaderFilter.bind(this));
this.registerComponentFunction("column", "getHeaderFilterValue", this.getHeaderFilterValue.bind(this));
this.registerComponentFunction("column", "setHeaderFilterValue", this.setHeaderFilterValue.bind(this));
}
initialize(){
this.subscribe("column-init", this.initializeColumnHeaderFilter.bind(this));
this.subscribe("column-width-fit-before", this.hideHeaderFilterElements.bind(this));
this.subscribe("column-width-fit-after", this.showHeaderFilterElements.bind(this));
this.subscribe("table-built", this.tableBuilt.bind(this));
if(this.table.options.filterMode === "remote"){
this.subscribe("data-params", this.remoteFilterParams.bind(this));
}
this.registerDataHandler(this.filter.bind(this), 10);
}
tableBuilt(){
if(this.table.options.initialFilter){
this.setFilter(this.table.options.initialFilter);
}
if(this.table.options.initialHeaderFilter){
this.table.options.initialHeaderFilter.forEach((item) => {
var column = this.table.columnManager.findColumn(item.field);
if(column){
this.setHeaderFilterValue(column, item.value);
}else {
console.warn("Column Filter Error - No matching column found:", item.field);
return false;
}
});
}
this.tableInitialized = true;
}
remoteFilterParams(data, config, silent, params){
params.filter = this.getFilters(true, true);
return params;
}
///////////////////////////////////
///////// Table Functions /////////
///////////////////////////////////
//set standard filters
userSetFilter(field, type, value, params){
this.setFilter(field, type, value, params);
this.refreshFilter();
}
//set standard filters
userRefreshFilter(){
this.refreshFilter();
}
//add filter to array
userAddFilter(field, type, value, params){
this.addFilter(field, type, value, params);
this.refreshFilter();
}
userSetHeaderFilterFocus(field){
var column = this.table.columnManager.findColumn(field);
if(column){
this.setHeaderFilterFocus(column);
}else {
console.warn("Column Filter Focus Error - No matching column found:", field);
return false;
}
}
userGetHeaderFilterValue(field) {
var column = this.table.columnManager.findColumn(field);
if(column){
return this.getHeaderFilterValue(column);
}else {
console.warn("Column Filter Error - No matching column found:", field);
}
}
userSetHeaderFilterValue(field, value){
var column = this.table.columnManager.findColumn(field);
if(column){
this.setHeaderFilterValue(column, value);
}else {
console.warn("Column Filter Error - No matching column found:", field);
return false;
}
}
//remove filter from array
userRemoveFilter(field, type, value){
this.removeFilter(field, type, value);
this.refreshFilter();
}
//clear filters
userClearFilter(all){
this.clearFilter(all);
this.refreshFilter();
}
//clear header filters
userClearHeaderFilter(){
this.clearHeaderFilter();
this.refreshFilter();
}
//search for specific row components
searchRows(field, type, value){
return this.search("rows", field, type, value);
}
//search for specific data
searchData(field, type, value){
return this.search("data", field, type, value);
}
///////////////////////////////////
///////// Internal Logic //////////
///////////////////////////////////
initializeColumnHeaderFilter(column){
var def = column.definition;
if(def.headerFilter){
this.initializeColumn(column);
}
}
//initialize column header filter
initializeColumn(column, value){
var self = this,
field = column.getField();
//handle successfully value change
function success(value){
var filterType = (column.modules.filter.tagType == "input" && column.modules.filter.attrType == "text") || column.modules.filter.tagType == "textarea" ? "partial" : "match",
type = "",
filterChangeCheck = "",
filterFunc;
if(typeof column.modules.filter.prevSuccess === "undefined" || column.modules.filter.prevSuccess !== value){
column.modules.filter.prevSuccess = value;
if(!column.modules.filter.emptyFunc(value)){
column.modules.filter.value = value;
switch(typeof column.definition.headerFilterFunc){
case "string":
if(Filter.filters[column.definition.headerFilterFunc]){
type = column.definition.headerFilterFunc;
filterFunc = function(data){
var params = column.definition.headerFilterFuncParams || {};
var fieldVal = column.getFieldValue(data);
params = typeof params === "function" ? params(value, fieldVal, data) : params;
return Filter.filters[column.definition.headerFilterFunc](value, fieldVal, data, params);
};
}else {
console.warn("Header Filter Error - Matching filter function not found: ", column.definition.headerFilterFunc);
}
break;
case "function":
filterFunc = function(data){
var params = column.definition.headerFilterFuncParams || {};
var fieldVal = column.getFieldValue(data);
params = typeof params === "function" ? params(value, fieldVal, data) : params;
return column.definition.headerFilterFunc(value, fieldVal, data, params);
};
type = filterFunc;
break;
}
if(!filterFunc){
switch(filterType){
case "partial":
filterFunc = function(data){
var colVal = column.getFieldValue(data);
if(typeof colVal !== 'undefined' && colVal !== null){
return String(colVal).toLowerCase().indexOf(String(value).toLowerCase()) > -1;
}else {
return false;
}
};
type = "like";
break;
default:
filterFunc = function(data){
return column.getFieldValue(data) == value;
};
type = "=";
}
}
self.headerFilters[field] = {value:value, func:filterFunc, type:type};
}else {
delete self.headerFilters[field];
}
column.modules.filter.value = value;
filterChangeCheck = JSON.stringify(self.headerFilters);
if(self.prevHeaderFilterChangeCheck !== filterChangeCheck){
self.prevHeaderFilterChangeCheck = filterChangeCheck;
self.trackChanges();
self.refreshFilter();
}
}
return true;
}
column.modules.filter = {
success:success,
attrType:false,
tagType:false,
emptyFunc:false,
};
this.generateHeaderFilterElement(column);
}
generateHeaderFilterElement(column, initialValue, reinitialize){
var self = this,
success = column.modules.filter.success,
field = column.getField(),
filterElement, editor, editorElement, cellWrapper, typingTimer, searchTrigger, params, onRenderedCallback;
column.modules.filter.value = initialValue;
//handle aborted edit
function cancel(){}
function onRendered(callback){
onRenderedCallback = callback;
}
if(column.modules.filter.headerElement && column.modules.filter.headerElement.parentNode){
column.contentElement.removeChild(column.modules.filter.headerElement.parentNode);
}
if(field){
//set empty value function
column.modules.filter.emptyFunc = column.definition.headerFilterEmptyCheck || function(value){
return !value && value !== 0;
};
filterElement = document.createElement("div");
filterElement.classList.add("tabulator-header-filter");
//set column editor
switch(typeof column.definition.headerFilter){
case "string":
if(self.table.modules.edit.editors[column.definition.headerFilter]){
editor = self.table.modules.edit.editors[column.definition.headerFilter];
if((column.definition.headerFilter === "tick" || column.definition.headerFilter === "tickCross") && !column.definition.headerFilterEmptyCheck){
column.modules.filter.emptyFunc = function(value){
return value !== true && value !== false;
};
}
}else {
console.warn("Filter Error - Cannot build header filter, No such editor found: ", column.definition.editor);
}
break;
case "function":
editor = column.definition.headerFilter;
break;
case "boolean":
if(column.modules.edit && column.modules.edit.editor){
editor = column.modules.edit.editor;
}else {
if(column.definition.formatter && self.table.modules.edit.editors[column.definition.formatter]){
editor = self.table.modules.edit.editors[column.definition.formatter];
if((column.definition.formatter === "tick" || column.definition.formatter === "tickCross") && !column.definition.headerFilterEmptyCheck){
column.modules.filter.emptyFunc = function(value){
return value !== true && value !== false;
};
}
}else {
editor = self.table.modules.edit.editors["input"];
}
}
break;
}
if(editor){
cellWrapper = {
getValue:function(){
return typeof initialValue !== "undefined" ? initialValue : "";
},
getField:function(){
return column.definition.field;
},
getElement:function(){
return filterElement;
},
getColumn:function(){
return column.getComponent();
},
getTable:() => {
return this.table;
},
getRow:function(){
return {
normalizeHeight:function(){
}
};
}
};
params = column.definition.headerFilterParams || {};
params = typeof params === "function" ? params.call(self.table, cellWrapper) : params;
editorElement = editor.call(this.table.modules.edit, cellWrapper, onRendered, success, cancel, params);
if(!editorElement){
console.warn("Filter Error - Cannot add filter to " + field + " column, editor returned a value of false");
return;
}
if(!(editorElement instanceof Node)){
console.warn("Filter Error - Cannot add filter to " + field + " column, editor should return an instance of Node, the editor returned:", editorElement);
return;
}
//set Placeholder Text
self.langBind("headerFilters|columns|" + column.definition.field, function(value){
editorElement.setAttribute("placeholder", typeof value !== "undefined" && value ? value : (column.definition.headerFilterPlaceholder || self.langText("headerFilters|default")));
});
//focus on element on click
editorElement.addEventListener("click", function(e){
e.stopPropagation();
editorElement.focus();
});
editorElement.addEventListener("focus", (e) => {
var left = this.table.columnManager.contentsElement.scrollLeft;
var headerPos = this.table.rowManager.element.scrollLeft;
if(left !== headerPos){
this.table.rowManager.scrollHorizontal(left);
this.table.columnManager.scrollHorizontal(left);
}
});
//live update filters as user types
typingTimer = false;
searchTrigger = function(e){
if(typingTimer){
clearTimeout(typingTimer);
}
typingTimer = setTimeout(function(){
success(editorElement.value);
},self.table.options.headerFilterLiveFilterDelay);
};
column.modules.filter.headerElement = editorElement;
column.modules.filter.attrType = editorElement.hasAttribute("type") ? editorElement.getAttribute("type").toLowerCase() : "" ;
column.modules.filter.tagType = editorElement.tagName.toLowerCase();
if(column.definition.headerFilterLiveFilter !== false){
if (
!(
column.definition.headerFilter === 'autocomplete' ||
column.definition.headerFilter === 'tickCross' ||
((column.definition.editor === 'autocomplete' ||
column.definition.editor === 'tickCross') &&
column.definition.headerFilter === true)
)
) {
editorElement.addEventListener("keyup", searchTrigger);
editorElement.addEventListener("search", searchTrigger);
//update number filtered columns on change
if(column.modules.filter.attrType == "number"){
editorElement.addEventListener("change", function(e){
success(editorElement.value);
});
}
//change text inputs to search inputs to allow for clearing of field
if(column.modules.filter.attrType == "text" && this.table.browser !== "ie"){
editorElement.setAttribute("type", "search");
// editorElement.off("change blur"); //prevent blur from triggering filter and preventing selection click
}
}
//prevent input and select elements from propagating click to column sorters etc
if(column.modules.filter.tagType == "input" || column.modules.filter.tagType == "select" || column.modules.filter.tagType == "textarea"){
editorElement.addEventListener("mousedown",function(e){
e.stopPropagation();
});
}
}
filterElement.appendChild(editorElement);
column.contentElement.appendChild(filterElement);
if(!reinitialize){
self.headerFilterColumns.push(column);
}
if(onRenderedCallback){
onRenderedCallback();
}
}
}else {
console.warn("Filter Error - Cannot add header filter, column has no field set:", column.definition.title);
}
}
//hide all header filter elements (used to ensure correct column widths in "fitData" layout mode)
hideHeaderFilterElements(){
this.headerFilterColumns.forEach(function(column){
if(column.modules.filter && column.modules.filter.headerElement){
column.modules.filter.headerElement.style.display = 'none';
}
});
}
//show all header filter elements (used to ensure correct column widths in "fitData" layout mode)
showHeaderFilterElements(){
this.headerFilterColumns.forEach(function(column){
if(column.modules.filter && column.modules.filter.headerElement){
column.modules.filter.headerElement.style.display = '';
}
});
}
//programmatically set focus of header filter
setHeaderFilterFocus(column){
if(column.modules.filter && column.modules.filter.headerElement){
column.modules.filter.headerElement.focus();
}else {
console.warn("Column Filter Focus Error - No header filter set on column:", column.getField());
}
}
//programmatically get value of header filter
getHeaderFilterValue(column){
if(column.modules.filter && column.modules.filter.headerElement){
return column.modules.filter.value;
} else {
console.warn("Column Filter Error - No header filter set on column:", column.getField());
}
}
//programmatically set value of header filter
setHeaderFilterValue(column, value){
if (column){
if(column.modules.filter && column.modules.filter.headerElement){
this.generateHeaderFilterElement(column, value, true);
column.modules.filter.success(value);
}else {
console.warn("Column Filter Error - No header filter set on column:", column.getField());
}
}
}
reloadHeaderFilter(column){
if (column){
if(column.modules.filter && column.modules.filter.headerElement){
this.generateHeaderFilterElement(column, column.modules.filter.value, true);
}else {
console.warn("Column Filter Error - No header filter set on column:", column.getField());
}
}
}
refreshFilter(){
if(this.tableInitialized){
if(this.table.options.filterMode === "remote"){
this.reloadData(null, false, false);
}else {
this.refreshData(true);
}
}
//TODO - Persist left position of row manager
// left = this.scrollLeft;
// this.scrollHorizontal(left);
}
//check if the filters has changed since last use
trackChanges(){
this.changed = true;
this.dispatch("filter-changed");
}
//check if the filters has changed since last use
hasChanged(){
var changed = this.changed;
this.changed = false;
return changed;
}
//set standard filters
setFilter(field, type, value, params){
this.filterList = [];
if(!Array.isArray(field)){
field = [{field:field, type:type, value:value, params:params}];
}
this.addFilter(field);
}
//add filter to array
addFilter(field, type, value, params){
var changed = false;
if(!Array.isArray(field)){
field = [{field:field, type:type, value:value, params:params}];
}
field.forEach((filter) => {
filter = this.findFilter(filter);
if(filter){
this.filterList.push(filter);
changed = true;
}
});
if(changed){
this.trackChanges();
}
}
findFilter(filter){
var column;
if(Array.isArray(filter)){
return this.findSubFilters(filter);
}
var filterFunc = false;
if(typeof filter.field == "function"){
filterFunc = function(data){
return filter.field(data, filter.type || {});// pass params to custom filter function
};
}else {
if(Filter.filters[filter.type]){
column = this.table.columnManager.getColumnByField(filter.field);
if(column){
filterFunc = function(data){
return Filter.filters[filter.type](filter.value, column.getFieldValue(data), data, filter.params || {});
};
}else {
filterFunc = function(data){
return Filter.filters[filter.type](filter.value, data[filter.field], data, filter.params || {});
};
}
}else {
console.warn("Filter Error - No such filter type found, ignoring: ", filter.type);
}
}
filter.func = filterFunc;
return filter.func ? filter : false;
}
findSubFilters(filters){
var output = [];
filters.forEach((filter) => {
filter = this.findFilter(filter);
if(filter){
output.push(filter);
}
});
return output.length ? output : false;
}
//get all filters
getFilters(all, ajax){
var output = [];
if(all){
output = this.getHeaderFilters();
}
if(ajax){
output.forEach(function(item){
if(typeof item.type == "function"){
item.type = "function";
}
});
}
output = output.concat(this.filtersToArray(this.filterList, ajax));
return output;
}
//filter to Object
filtersToArray(filterList, ajax){
var output = [];
filterList.forEach((filter) => {
var item;
if(Array.isArray(filter)){
output.push(this.filtersToArray(filter, ajax));
}else {
item = {field:filter.field, type:filter.type, value:filter.value};
if(ajax){
if(typeof item.type == "function"){
item.type = "function";
}
}
output.push(item);
}
});
return output;
}
//get all filters
getHeaderFilters(){
var output = [];
for(var key in this.headerFilters){
output.push({field:key, type:this.headerFilters[key].type, value:this.headerFilters[key].value});
}
return output;
}
//remove filter from array
removeFilter(field, type, value){
if(!Array.isArray(field)){
field = [{field:field, type:type, value:value}];
}
field.forEach((filter) => {
var index = -1;
if(typeof filter.field == "object"){
index = this.filterList.findIndex((element) => {
return filter === element;
});
}else {
index = this.filterList.findIndex((element) => {
return filter.field === element.field && filter.type === element.type && filter.value === element.value;
});
}
if(index > -1){
this.filterList.splice(index, 1);
}else {
console.warn("Filter Error - No matching filter type found, ignoring: ", filter.type);
}
});
this.trackChanges();
}
//clear filters
clearFilter(all){
this.filterList = [];
if(all){
this.clearHeaderFilter();
}
this.trackChanges();
}
//clear header filters
clearHeaderFilter(){
this.headerFilters = {};
this.prevHeaderFilterChangeCheck = "{}";
this.headerFilterColumns.forEach((column) => {
if(typeof column.modules.filter.value !== "undefined"){
delete column.modules.filter.value;
}
column.modules.filter.prevSuccess = undefined;
this.reloadHeaderFilter(column);
});
this.trackChanges();
}
//search data and return matching rows
search (searchType, field, type, value){
var activeRows = [],
filterList = [];
if(!Array.isArray(field)){
field = [{field:field, type:type, value:value}];
}
field.forEach((filter) => {
filter = this.findFilter(filter);
if(filter){
filterList.push(filter);
}
});
this.table.rowManager.rows.forEach((row) => {
var match = true;
filterList.forEach((filter) => {
if(!this.filterRecurse(filter, row.getData())){
match = false;
}
});
if(match){
activeRows.push(searchType === "data" ? row.getData("data") : row.getComponent());
}
});
return activeRows;
}
//filter row array
filter(rowList, filters){
var activeRows = [],
activeRowComponents = [];
if(this.subscribedExternal("dataFiltering")){
this.dispatchExternal("dataFiltering", this.getFilters(true));
}
if(this.table.options.filterMode !== "remote" && (this.filterList.length || Object.keys(this.headerFilters).length)){
rowList.forEach((row) => {
if(this.filterRow(row)){
activeRows.push(row);
}
});
}else {
activeRows = rowList.slice(0);
}
if(this.subscribedExternal("dataFiltered")){
activeRows.forEach((row) => {
activeRowComponents.push(row.getComponent());
});
this.dispatchExternal("dataFiltered", this.getFilters(true), activeRowComponents);
}
return activeRows;
}
//filter individual row
filterRow(row, filters){
var match = true,
data = row.getData();
this.filterList.forEach((filter) => {
if(!this.filterRecurse(filter, data)){
match = false;
}
});
for(var field in this.headerFilters){
if(!this.headerFilters[field].func(data)){
match = false;
}
}
return match;
}
filterRecurse(filter, data){
var match = false;
if(Array.isArray(filter)){
filter.forEach((subFilter) => {
if(this.filterRecurse(subFilter, data)){
match = true;
}
});
}else {
match = filter.func(data);
}
return match;
}
}
Filter.moduleName = "filter";
//load defaults
Filter.filters = defaultFilters;
function plaintext(cell, formatterParams, onRendered){
return this.emptyToSpace(this.sanitizeHTML(cell.getValue()));
}
function html$1(cell, formatterParams, onRendered){
return cell.getValue();
}
function textarea$1(cell, formatterParams, onRendered){
cell.getElement().style.whiteSpace = "pre-wrap";
return this.emptyToSpace(this.sanitizeHTML(cell.getValue()));
}
function money(cell, formatterParams, onRendered){
var floatVal = parseFloat(cell.getValue()),
sign = "",
number, integer, decimal, rgx;
var decimalSym = formatterParams.decimal || ".";
var thousandSym = formatterParams.thousand || ",";
var negativeSign = formatterParams.negativeSign || "-";
var symbol = formatterParams.symbol || "";
var after = !!formatterParams.symbolAfter;
var precision = typeof formatterParams.precision !== "undefined" ? formatterParams.precision : 2;
if(isNaN(floatVal)){
return this.emptyToSpace(this.sanitizeHTML(cell.getValue()));
}
if(floatVal < 0){
floatVal = Math.abs(floatVal);
sign = negativeSign;
}
number = precision !== false ? floatVal.toFixed(precision) : floatVal;
number = String(number).split(".");
integer = number[0];
decimal = number.length > 1 ? decimalSym + number[1] : "";
if (formatterParams.thousand !== false) {
rgx = /(\d+)(\d{3})/;
while (rgx.test(integer)){
integer = integer.replace(rgx, "$1" + thousandSym + "$2");
}
}
return after ? sign + integer + decimal + symbol : sign + symbol + integer + decimal;
}
function link(cell, formatterParams, onRendered){
var value = cell.getValue(),
urlPrefix = formatterParams.urlPrefix || "",
download = formatterParams.download,
label = value,
el = document.createElement("a"),
data;
function labelTraverse(path, data){
var item = path.shift(),
value = data[item];
if(path.length && typeof value === "object"){
return labelTraverse(path, value);
}
return value;
}
if(formatterParams.labelField){
data = cell.getData();
label = labelTraverse(formatterParams.labelField.split(this.table.options.nestedFieldSeparator), data);
}
if(formatterParams.label){
switch(typeof formatterParams.label){
case "string":
label = formatterParams.label;
break;
case "function":
label = formatterParams.label(cell);
break;
}
}
if(label){
if(formatterParams.urlField){
data = cell.getData();
value = data[formatterParams.urlField];
}
if(formatterParams.url){
switch(typeof formatterParams.url){
case "string":
value = formatterParams.url;
break;
case "function":
value = formatterParams.url(cell);
break;
}
}
el.setAttribute("href", urlPrefix + value);
if(formatterParams.target){
el.setAttribute("target", formatterParams.target);
}
if(formatterParams.download){
if(typeof download == "function"){
download = download(cell);
}else {
download = download === true ? "" : download;
}
el.setAttribute("download", download);
}
el.innerHTML = this.emptyToSpace(this.sanitizeHTML(label));
return el;
}else {
return " ";
}
}
function image(cell, formatterParams, onRendered){
var el = document.createElement("img"),
src = cell.getValue();
if(formatterParams.urlPrefix){
src = formatterParams.urlPrefix + cell.getValue();
}
if(formatterParams.urlSuffix){
src = src + formatterParams.urlSuffix;
}
el.setAttribute("src", src);
switch(typeof formatterParams.height){
case "number":
el.style.height = formatterParams.height + "px";
break;
case "string":
el.style.height = formatterParams.height;
break;
}
switch(typeof formatterParams.width){
case "number":
el.style.width = formatterParams.width + "px";
break;
case "string":
el.style.width = formatterParams.width;
break;
}
el.addEventListener("load", function(){
cell.getRow().normalizeHeight();
});
return el;
}
function tickCross$1(cell, formatterParams, onRendered){
var value = cell.getValue(),
element = cell.getElement(),
empty = formatterParams.allowEmpty,
truthy = formatterParams.allowTruthy,
trueValueSet = Object.keys(formatterParams).includes("trueValue"),
tick = typeof formatterParams.tickElement !== "undefined" ? formatterParams.tickElement : '',
cross = typeof formatterParams.crossElement !== "undefined" ? formatterParams.crossElement : '';
if((trueValueSet && value === formatterParams.trueValue) || (!trueValueSet && ((truthy && value) || (value === true || value === "true" || value === "True" || value === 1 || value === "1")))){
element.setAttribute("aria-checked", true);
return tick || "";
}else {
if(empty && (value === "null" || value === "" || value === null || typeof value === "undefined")){
element.setAttribute("aria-checked", "mixed");
return "";
}else {
element.setAttribute("aria-checked", false);
return cross || "";
}
}
}
function datetime$1(cell, formatterParams, onRendered){
var DT = window.DateTime || luxon.DateTime;
var inputFormat = formatterParams.inputFormat || "yyyy-MM-dd HH:mm:ss";
var outputFormat = formatterParams.outputFormat || "dd/MM/yyyy HH:mm:ss";
var invalid = typeof formatterParams.invalidPlaceholder !== "undefined" ? formatterParams.invalidPlaceholder : "";
var value = cell.getValue();
if(typeof DT != "undefined"){
var newDatetime;
if(DT.isDateTime(value)){
newDatetime = value;
}else if(inputFormat === "iso"){
newDatetime = DT.fromISO(String(value));
}else {
newDatetime = DT.fromFormat(String(value), inputFormat);
}
if(newDatetime.isValid){
if(formatterParams.timezone){
newDatetime = newDatetime.setZone(formatterParams.timezone);
}
return newDatetime.toFormat(outputFormat);
}else {
if(invalid === true || !value){
return value;
}else if(typeof invalid === "function"){
return invalid(value);
}else {
return invalid;
}
}
}else {
console.error("Format Error - 'datetime' formatter is dependant on luxon.js");
}
}
function datetimediff (cell, formatterParams, onRendered) {
var DT = window.DateTime || luxon.DateTime;
var inputFormat = formatterParams.inputFormat || "yyyy-MM-dd HH:mm:ss";
var invalid = typeof formatterParams.invalidPlaceholder !== "undefined" ? formatterParams.invalidPlaceholder : "";
var suffix = typeof formatterParams.suffix !== "undefined" ? formatterParams.suffix : false;
var unit = typeof formatterParams.unit !== "undefined" ? formatterParams.unit : "days";
var humanize = typeof formatterParams.humanize !== "undefined" ? formatterParams.humanize : false;
var date = typeof formatterParams.date !== "undefined" ? formatterParams.date : DT.now();
var value = cell.getValue();
if(typeof DT != "undefined"){
var newDatetime;
if(DT.isDateTime(value)){
newDatetime = value;
}else if(inputFormat === "iso"){
newDatetime = DT.fromISO(String(value));
}else {
newDatetime = DT.fromFormat(String(value), inputFormat);
}
if (newDatetime.isValid){
if(humanize){
return newDatetime.diff(date, unit).toHuman() + (suffix ? " " + suffix : "");
}else {
return parseInt(newDatetime.diff(date, unit)[unit]) + (suffix ? " " + suffix : "");
}
} else {
if (invalid === true) {
return value;
} else if (typeof invalid === "function") {
return invalid(value);
} else {
return invalid;
}
}
}else {
console.error("Format Error - 'datetimediff' formatter is dependant on luxon.js");
}
}
function lookup (cell, formatterParams, onRendered) {
var value = cell.getValue();
if (typeof formatterParams[value] === "undefined") {
console.warn('Missing display value for ' + value);
return value;
}
return formatterParams[value];
}
function star$1(cell, formatterParams, onRendered){
var value = cell.getValue(),
element = cell.getElement(),
maxStars = formatterParams && formatterParams.stars ? formatterParams.stars : 5,
stars = document.createElement("span"),
star = document.createElementNS('http://www.w3.org/2000/svg', "svg"),
starActive = '',
starInactive = '';
//style stars holder
stars.style.verticalAlign = "middle";
//style star
star.setAttribute("width", "14");
star.setAttribute("height", "14");
star.setAttribute("viewBox", "0 0 512 512");
star.setAttribute("xml:space", "preserve");
star.style.padding = "0 1px";
value = value && !isNaN(value) ? parseInt(value) : 0;
value = Math.max(0, Math.min(value, maxStars));
for(var i=1;i<= maxStars;i++){
var nextStar = star.cloneNode(true);
nextStar.innerHTML = i <= value ? starActive : starInactive;
stars.appendChild(nextStar);
}
element.style.whiteSpace = "nowrap";
element.style.overflow = "hidden";
element.style.textOverflow = "ellipsis";
element.setAttribute("aria-label", value);
return stars;
}
function traffic(cell, formatterParams, onRendered){
var value = this.sanitizeHTML(cell.getValue()) || 0,
el = document.createElement("span"),
max = formatterParams && formatterParams.max ? formatterParams.max : 100,
min = formatterParams && formatterParams.min ? formatterParams.min : 0,
colors = formatterParams && typeof formatterParams.color !== "undefined" ? formatterParams.color : ["red", "orange", "green"],
color = "#666666",
percent, percentValue;
if(isNaN(value) || typeof cell.getValue() === "undefined"){
return;
}
el.classList.add("tabulator-traffic-light");
//make sure value is in range
percentValue = parseFloat(value) <= max ? parseFloat(value) : max;
percentValue = parseFloat(percentValue) >= min ? parseFloat(percentValue) : min;
//workout percentage
percent = (max - min) / 100;
percentValue = Math.round((percentValue - min) / percent);
//set color
switch(typeof colors){
case "string":
color = colors;
break;
case "function":
color = colors(value);
break;
case "object":
if(Array.isArray(colors)){
var unit = 100 / colors.length;
var index = Math.floor(percentValue / unit);
index = Math.min(index, colors.length - 1);
index = Math.max(index, 0);
color = colors[index];
break;
}
}
el.style.backgroundColor = color;
return el;
}
function progress$1(cell, formatterParams = {}, onRendered){ //progress bar
var value = this.sanitizeHTML(cell.getValue()) || 0,
element = cell.getElement(),
max = formatterParams.max ? formatterParams.max : 100,
min = formatterParams.min ? formatterParams.min : 0,
legendAlign = formatterParams.legendAlign ? formatterParams.legendAlign : "center",
percent, percentValue, color, legend, legendColor;
//make sure value is in range
percentValue = parseFloat(value) <= max ? parseFloat(value) : max;
percentValue = parseFloat(percentValue) >= min ? parseFloat(percentValue) : min;
//workout percentage
percent = (max - min) / 100;
percentValue = Math.round((percentValue - min) / percent);
//set bar color
switch(typeof formatterParams.color){
case "string":
color = formatterParams.color;
break;
case "function":
color = formatterParams.color(value);
break;
case "object":
if(Array.isArray(formatterParams.color)){
let unit = 100 / formatterParams.color.length;
let index = Math.floor(percentValue / unit);
index = Math.min(index, formatterParams.color.length - 1);
index = Math.max(index, 0);
color = formatterParams.color[index];
break;
}
default:
color = "#2DC214";
}
//generate legend
switch(typeof formatterParams.legend){
case "string":
legend = formatterParams.legend;
break;
case "function":
legend = formatterParams.legend(value);
break;
case "boolean":
legend = value;
break;
default:
legend = false;
}
//set legend color
switch(typeof formatterParams.legendColor){
case "string":
legendColor = formatterParams.legendColor;
break;
case "function":
legendColor = formatterParams.legendColor(value);
break;
case "object":
if(Array.isArray(formatterParams.legendColor)){
let unit = 100 / formatterParams.legendColor.length;
let index = Math.floor(percentValue / unit);
index = Math.min(index, formatterParams.legendColor.length - 1);
index = Math.max(index, 0);
legendColor = formatterParams.legendColor[index];
}
break;
default:
legendColor = "#000";
}
element.style.minWidth = "30px";
element.style.position = "relative";
element.setAttribute("aria-label", percentValue);
var barEl = document.createElement("div");
barEl.style.display = "inline-block";
barEl.style.width = percentValue + "%";
barEl.style.backgroundColor = color;
barEl.style.height = "100%";
barEl.setAttribute('data-max', max);
barEl.setAttribute('data-min', min);
var barContainer = document.createElement("div");
barContainer.style.position = "relative";
barContainer.style.width = "100%";
barContainer.style.height = "100%";
if(legend){
var legendEl = document.createElement("div");
legendEl.style.position = "absolute";
legendEl.style.top = 0;
legendEl.style.left = 0;
legendEl.style.textAlign = legendAlign;
legendEl.style.width = "100%";
legendEl.style.color = legendColor;
legendEl.innerHTML = legend;
}
onRendered(function(){
//handle custom element needed if formatter is to be included in printed/downloaded output
if(!(cell instanceof CellComponent)){
var holderEl = document.createElement("div");
holderEl.style.position = "absolute";
holderEl.style.top = "4px";
holderEl.style.bottom = "4px";
holderEl.style.left = "4px";
holderEl.style.right = "4px";
element.appendChild(holderEl);
element = holderEl;
}
element.appendChild(barContainer);
barContainer.appendChild(barEl);
if(legend){
barContainer.appendChild(legendEl);
}
});
return "";
}
function color(cell, formatterParams, onRendered){
cell.getElement().style.backgroundColor = this.sanitizeHTML(cell.getValue());
return "";
}
function buttonTick(cell, formatterParams, onRendered){
return '';
}
function buttonCross(cell, formatterParams, onRendered){
return '';
}
function rownum(cell, formatterParams, onRendered){
var content = document.createElement("span");
var row = cell.getRow();
row.watchPosition((position) => {
content.innerText = position;
});
return content;
}
function handle(cell, formatterParams, onRendered){
cell.getElement().classList.add("tabulator-row-handle");
return "";
}
function responsiveCollapse(cell, formatterParams, onRendered){
var el = document.createElement("div"),
config = cell.getRow()._row.modules.responsiveLayout;
el.classList.add("tabulator-responsive-collapse-toggle");
el.innerHTML = `
`;
cell.getElement().classList.add("tabulator-row-handle");
function toggleList(isOpen){
var collapseEl = config.element;
config.open = isOpen;
if(collapseEl){
if(config.open){
el.classList.add("open");
collapseEl.style.display = '';
}else {
el.classList.remove("open");
collapseEl.style.display = 'none';
}
}
}
el.addEventListener("click", function(e){
e.stopImmediatePropagation();
toggleList(!config.open);
cell.getTable().rowManager.adjustTableSize();
});
toggleList(config.open);
return el;
}
function rowSelection(cell, formatterParams, onRendered){
var checkbox = document.createElement("input");
var blocked = false;
checkbox.type = 'checkbox';
checkbox.setAttribute("aria-label", "Select Row");
if(this.table.modExists("selectRow", true)){
checkbox.addEventListener("click", (e) => {
e.stopPropagation();
});
if(typeof cell.getRow == 'function'){
var row = cell.getRow();
if(row instanceof RowComponent){
checkbox.addEventListener("change", (e) => {
if(this.table.options.selectableRangeMode === "click"){
if(!blocked){
row.toggleSelect();
}else {
blocked = false;
}
}else {
row.toggleSelect();
}
});
if(this.table.options.selectableRangeMode === "click"){
checkbox.addEventListener("click", (e) => {
blocked = true;
this.table.modules.selectRow.handleComplexRowClick(row._row, e);
});
}
checkbox.checked = row.isSelected && row.isSelected();
this.table.modules.selectRow.registerRowSelectCheckbox(row, checkbox);
}else {
checkbox = "";
}
}else {
checkbox.addEventListener("change", (e) => {
if(this.table.modules.selectRow.selectedRows.length){
this.table.deselectRow();
}else {
this.table.selectRow(formatterParams.rowRange);
}
});
this.table.modules.selectRow.registerHeaderSelectCheckbox(checkbox);
}
}
return checkbox;
}
var defaultFormatters = {
plaintext:plaintext,
html:html$1,
textarea:textarea$1,
money:money,
link:link,
image:image,
tickCross:tickCross$1,
datetime:datetime$1,
datetimediff:datetimediff,
lookup:lookup,
star:star$1,
traffic:traffic,
progress:progress$1,
color:color,
buttonTick:buttonTick,
buttonCross:buttonCross,
rownum:rownum,
handle:handle,
responsiveCollapse:responsiveCollapse,
rowSelection:rowSelection,
};
class Format extends Module{
constructor(table){
super(table);
this.registerColumnOption("formatter");
this.registerColumnOption("formatterParams");
this.registerColumnOption("formatterPrint");
this.registerColumnOption("formatterPrintParams");
this.registerColumnOption("formatterClipboard");
this.registerColumnOption("formatterClipboardParams");
this.registerColumnOption("formatterHtmlOutput");
this.registerColumnOption("formatterHtmlOutputParams");
this.registerColumnOption("titleFormatter");
this.registerColumnOption("titleFormatterParams");
}
initialize(){
this.subscribe("cell-format", this.formatValue.bind(this));
this.subscribe("cell-rendered", this.cellRendered.bind(this));
this.subscribe("column-layout", this.initializeColumn.bind(this));
this.subscribe("column-format", this.formatHeader.bind(this));
}
//initialize column formatter
initializeColumn(column){
column.modules.format = this.lookupFormatter(column, "");
if(typeof column.definition.formatterPrint !== "undefined"){
column.modules.format.print = this.lookupFormatter(column, "Print");
}
if(typeof column.definition.formatterClipboard !== "undefined"){
column.modules.format.clipboard = this.lookupFormatter(column, "Clipboard");
}
if(typeof column.definition.formatterHtmlOutput !== "undefined"){
column.modules.format.htmlOutput = this.lookupFormatter(column, "HtmlOutput");
}
}
lookupFormatter(column, type){
var config = {params:column.definition["formatter" + type + "Params"] || {}},
formatter = column.definition["formatter" + type];
//set column formatter
switch(typeof formatter){
case "string":
if(Format.formatters[formatter]){
config.formatter = Format.formatters[formatter];
}else {
console.warn("Formatter Error - No such formatter found: ", formatter);
config.formatter = Format.formatters.plaintext;
}
break;
case "function":
config.formatter = formatter;
break;
default:
config.formatter = Format.formatters.plaintext;
break;
}
return config;
}
cellRendered(cell){
if(cell.modules.format && cell.modules.format.renderedCallback && !cell.modules.format.rendered){
cell.modules.format.renderedCallback();
cell.modules.format.rendered = true;
}
}
//return a formatted value for a column header
formatHeader(column, title, el){
var formatter, params, onRendered, mockCell;
if(column.definition.titleFormatter){
formatter = this.getFormatter(column.definition.titleFormatter);
onRendered = (callback) => {
column.titleFormatterRendered = callback;
};
mockCell = {
getValue:function(){
return title;
},
getElement:function(){
return el;
},
getColumn:function(){
return column.getComponent();
},
getTable:() => {
return this.table;
}
};
params = column.definition.titleFormatterParams || {};
params = typeof params === "function" ? params() : params;
return formatter.call(this, mockCell, params, onRendered);
}else {
return title;
}
}
//return a formatted value for a cell
formatValue(cell){
var component = cell.getComponent(),
params = typeof cell.column.modules.format.params === "function" ? cell.column.modules.format.params(component) : cell.column.modules.format.params;
function onRendered(callback){
if(!cell.modules.format){
cell.modules.format = {};
}
cell.modules.format.renderedCallback = callback;
cell.modules.format.rendered = false;
}
return cell.column.modules.format.formatter.call(this, component, params, onRendered);
}
formatExportValue(cell, type){
var formatter = cell.column.modules.format[type],
params;
if(formatter){
params = typeof formatter.params === "function" ? formatter.params(cell.getComponent()) : formatter.params;
function onRendered(callback){
if(!cell.modules.format){
cell.modules.format = {};
}
cell.modules.format.renderedCallback = callback;
cell.modules.format.rendered = false;
}
return formatter.formatter.call(this, cell.getComponent(), params, onRendered);
}else {
return this.formatValue(cell);
}
}
sanitizeHTML(value){
if(value){
var entityMap = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": ''',
'/': '/',
'`': '`',
'=': '='
};
return String(value).replace(/[&<>"'`=/]/g, function (s) {
return entityMap[s];
});
}else {
return value;
}
}
emptyToSpace(value){
return value === null || typeof value === "undefined" || value === "" ? " " : value;
}
//get formatter for cell
getFormatter(formatter){
switch(typeof formatter){
case "string":
if(Format.formatters[formatter]){
formatter = Format.formatters[formatter];
}else {
console.warn("Formatter Error - No such formatter found: ", formatter);
formatter = Format.formatters.plaintext;
}
break;
case "function":
//Custom formatter Function, do nothing
break;
default:
formatter = Format.formatters.plaintext;
break;
}
return formatter;
}
}
Format.moduleName = "format";
//load defaults
Format.formatters = defaultFormatters;
class FrozenColumns extends Module{
constructor(table){
super(table);
this.leftColumns = [];
this.rightColumns = [];
this.initializationMode = "left";
this.active = false;
this.blocked = true;
this.registerColumnOption("frozen");
}
//reset initial state
reset(){
this.initializationMode = "left";
this.leftColumns = [];
this.rightColumns = [];
this.active = false;
}
initialize(){
this.subscribe("cell-layout", this.layoutCell.bind(this));
this.subscribe("column-init", this.initializeColumn.bind(this));
this.subscribe("column-width", this.layout.bind(this));
this.subscribe("row-layout-after", this.layoutRow.bind(this));
this.subscribe("table-layout", this.layout.bind(this));
this.subscribe("columns-loading", this.reset.bind(this));
this.subscribe("column-add", this.reinitializeColumns.bind(this));
this.subscribe("column-delete", this.reinitializeColumns.bind(this));
this.subscribe("table-redraw", this.layout.bind(this));
this.subscribe("layout-refreshing", this.blockLayout.bind(this));
this.subscribe("layout-refreshed", this.unblockLayout.bind(this));
this.subscribe("scrollbar-vertical", this.adjustForScrollbar.bind(this));
}
blockLayout(){
this.blocked = true;
}
unblockLayout(){
this.blocked = false;
}
layoutCell(cell){
this.layoutElement(cell.element, cell.column);
}
reinitializeColumns(){
this.reset();
this.table.columnManager.columnsByIndex.forEach((column) => {
this.initializeColumn(column);
});
}
//initialize specific column
initializeColumn(column){
var config = {margin:0, edge:false};
if(!column.isGroup){
if(this.frozenCheck(column)){
config.position = this.initializationMode;
if(this.initializationMode == "left"){
this.leftColumns.push(column);
}else {
this.rightColumns.unshift(column);
}
this.active = true;
column.modules.frozen = config;
}else {
this.initializationMode = "right";
}
}
}
frozenCheck(column){
if(column.parent.isGroup && column.definition.frozen){
console.warn("Frozen Column Error - Parent column group must be frozen, not individual columns or sub column groups");
}
if(column.parent.isGroup){
return this.frozenCheck(column.parent);
}else {
return column.definition.frozen;
}
}
//layout calculation rows
layoutCalcRows(){
if(this.table.modExists("columnCalcs")){
if(this.table.modules.columnCalcs.topInitialized && this.table.modules.columnCalcs.topRow){
this.layoutRow(this.table.modules.columnCalcs.topRow);
}
if(this.table.modules.columnCalcs.botInitialized && this.table.modules.columnCalcs.botRow){
this.layoutRow(this.table.modules.columnCalcs.botRow);
}
if(this.table.modExists("groupRows")){
this.layoutGroupCalcs(this.table.modules.groupRows.getGroups());
}
}
}
layoutGroupCalcs(groups){
groups.forEach((group) => {
if(group.calcs.top){
this.layoutRow(group.calcs.top);
}
if(group.calcs.bottom){
this.layoutRow(group.calcs.bottom);
}
if(group.groupList && group.groupList.length){
this.layoutGroupCalcs(group.groupList);
}
});
}
//calculate column positions and layout headers
layoutColumnPosition(allCells){
var leftParents = [];
var leftMargin = 0;
var rightMargin = 0;
this.leftColumns.forEach((column, i) => {
column.modules.frozen.marginValue = leftMargin;
column.modules.frozen.margin = column.modules.frozen.marginValue + "px";
if(column.visible){
leftMargin += column.getWidth();
}
if(i == this.leftColumns.length - 1){
column.modules.frozen.edge = true;
}else {
column.modules.frozen.edge = false;
}
if(column.parent.isGroup){
var parentEl = this.getColGroupParentElement(column);
if(!leftParents.includes(parentEl)){
this.layoutElement(parentEl, column);
leftParents.push(parentEl);
}
if(column.modules.frozen.edge){
parentEl.classList.add("tabulator-frozen-" + column.modules.frozen.position);
}
}else {
this.layoutElement(column.getElement(), column);
}
if(allCells){
column.cells.forEach((cell) => {
this.layoutElement(cell.getElement(true), column);
});
}
});
this.rightColumns.forEach((column, i) => {
column.modules.frozen.marginValue = rightMargin;
column.modules.frozen.margin = column.modules.frozen.marginValue + "px";
if(column.visible){
rightMargin += column.getWidth();
}
if(i == this.rightColumns.length - 1){
column.modules.frozen.edge = true;
}else {
column.modules.frozen.edge = false;
}
if(column.parent.isGroup){
this.layoutElement(this.getColGroupParentElement(column), column);
}else {
this.layoutElement(column.getElement(), column);
}
if(allCells){
column.cells.forEach((cell) => {
this.layoutElement(cell.getElement(true), column);
});
}
});
}
getColGroupParentElement(column){
return column.parent.isGroup ? this.getColGroupParentElement(column.parent) : column.getElement();
}
//layout columns appropriately
layout(){
if(this.active && !this.blocked){
//calculate left columns
this.layoutColumnPosition();
this.reinitializeRows();
this.layoutCalcRows();
}
}
reinitializeRows(){
var visibleRows = this.table.rowManager.getVisibleRows(true);
var otherRows = this.table.rowManager.getRows().filter(row => !visibleRows.includes(row));
otherRows.forEach((row) =>{
row.deinitialize();
});
visibleRows.forEach((row) =>{
if(row.type === "row"){
this.layoutRow(row);
}
});
}
layoutRow(row){
if(this.table.options.layout === "fitDataFill" && this.rightColumns.length){
this.table.rowManager.getTableElement().style.minWidth = "calc(100% - " + this.rightMargin + ")";
}
this.leftColumns.forEach((column) => {
var cell = row.getCell(column);
if(cell){
this.layoutElement(cell.getElement(true), column);
}
});
this.rightColumns.forEach((column) => {
var cell = row.getCell(column);
if(cell){
this.layoutElement(cell.getElement(true), column);
}
});
}
layoutElement(element, column){
var position;
if(column.modules.frozen){
element.style.position = "sticky";
if(this.table.rtl){
position = column.modules.frozen.position === "left" ? "right" : "left";
}else {
position = column.modules.frozen.position;
}
element.style[position] = column.modules.frozen.margin;
element.classList.add("tabulator-frozen");
if(column.modules.frozen.edge){
element.classList.add("tabulator-frozen-" + column.modules.frozen.position);
}
}
}
adjustForScrollbar(width){
if(this.rightColumns.length){
this.table.columnManager.getContentsElement().style.width = "calc(100% - " + width + "px)";
}
}
_calcSpace(columns, index){
var width = 0;
for (let i = 0; i < index; i++){
if(columns[i].visible){
width += columns[i].getWidth();
}
}
return width;
}
}
FrozenColumns.moduleName = "frozenColumns";
class FrozenRows extends Module{
constructor(table){
super(table);
this.topElement = document.createElement("div");
this.rows = [];
//register component functions
this.registerComponentFunction("row", "freeze", this.freezeRow.bind(this));
this.registerComponentFunction("row", "unfreeze", this.unfreezeRow.bind(this));
this.registerComponentFunction("row", "isFrozen", this.isRowFrozen.bind(this));
//register table options
this.registerTableOption("frozenRowsField", "id"); //field to choose frozen rows by
this.registerTableOption("frozenRows", false); //holder for frozen row identifiers
}
initialize(){
this.rows = [];
this.topElement.classList.add("tabulator-frozen-rows-holder");
// this.table.columnManager.element.append(this.topElement);
this.table.columnManager.getContentsElement().insertBefore(this.topElement, this.table.columnManager.headersElement.nextSibling);
this.subscribe("row-deleting", this.detachRow.bind(this));
this.subscribe("rows-visible", this.visibleRows.bind(this));
this.registerDisplayHandler(this.getRows.bind(this), 10);
if(this.table.options.frozenRows){
this.subscribe("data-processed", this.initializeRows.bind(this));
this.subscribe("row-added", this.initializeRow.bind(this));
this.subscribe("table-redrawing", this.resizeHolderWidth.bind(this));
this.subscribe("column-resized", this.resizeHolderWidth.bind(this));
this.subscribe("column-show", this.resizeHolderWidth.bind(this));
this.subscribe("column-hide", this.resizeHolderWidth.bind(this));
}
this.resizeHolderWidth();
}
resizeHolderWidth(){
this.topElement.style.minWidth = this.table.columnManager.headersElement.offsetWidth + "px";
}
initializeRows(){
this.table.rowManager.getRows().forEach((row) => {
this.initializeRow(row);
});
}
initializeRow(row){
var frozenRows = this.table.options.frozenRows,
rowType = typeof frozenRows;
if(rowType === "number"){
if(row.getPosition() && (row.getPosition() + this.rows.length) <= frozenRows){
this.freezeRow(row);
}
}else if(rowType === "function"){
if(frozenRows.call(this.table, row.getComponent())){
this.freezeRow(row);
}
}else if(Array.isArray(frozenRows)){
if(frozenRows.includes(row.data[this.options("frozenRowsField")])){
this.freezeRow(row);
}
}
}
isRowFrozen(row){
var index = this.rows.indexOf(row);
return index > -1;
}
isFrozen(){
return !!this.rows.length;
}
visibleRows(viewable, rows){
this.rows.forEach((row) => {
rows.push(row);
});
return rows;
}
//filter frozen rows out of display data
getRows(rows){
var output = rows.slice(0);
this.rows.forEach(function(row){
var index = output.indexOf(row);
if(index > -1){
output.splice(index, 1);
}
});
return output;
}
freezeRow(row){
if(!row.modules.frozen){
row.modules.frozen = true;
this.topElement.appendChild(row.getElement());
row.initialize();
row.normalizeHeight();
this.rows.push(row);
this.refreshData(false, "display");
this.table.rowManager.adjustTableSize();
this.styleRows();
}else {
console.warn("Freeze Error - Row is already frozen");
}
}
unfreezeRow(row){
if(row.modules.frozen){
row.modules.frozen = false;
this.detachRow(row);
this.table.rowManager.adjustTableSize();
this.refreshData(false, "display");
if(this.rows.length){
this.styleRows();
}
}else {
console.warn("Freeze Error - Row is already unfrozen");
}
}
detachRow(row){
var index = this.rows.indexOf(row);
if(index > -1){
var rowEl = row.getElement();
if(rowEl.parentNode){
rowEl.parentNode.removeChild(rowEl);
}
this.rows.splice(index, 1);
}
}
styleRows(row){
this.rows.forEach((row, i) => {
this.table.rowManager.styleRow(row, i);
});
}
}
FrozenRows.moduleName = "frozenRows";
//public group object
class GroupComponent {
constructor (group){
this._group = group;
this.type = "GroupComponent";
return new Proxy(this, {
get: function(target, name, receiver) {
if (typeof target[name] !== "undefined") {
return target[name];
}else {
return target._group.groupManager.table.componentFunctionBinder.handle("group", target._group, name);
}
}
});
}
getKey(){
return this._group.key;
}
getField(){
return this._group.field;
}
getElement(){
return this._group.element;
}
getRows(){
return this._group.getRows(true);
}
getSubGroups(){
return this._group.getSubGroups(true);
}
getParentGroup(){
return this._group.parent ? this._group.parent.getComponent() : false;
}
isVisible(){
return this._group.visible;
}
show(){
this._group.show();
}
hide(){
this._group.hide();
}
toggle(){
this._group.toggleVisibility();
}
_getSelf(){
return this._group;
}
getTable(){
return this._group.groupManager.table;
}
}
//Group functions
class Group{
constructor(groupManager, parent, level, key, field, generator, oldGroup){
this.groupManager = groupManager;
this.parent = parent;
this.key = key;
this.level = level;
this.field = field;
this.hasSubGroups = level < (groupManager.groupIDLookups.length - 1);
this.addRow = this.hasSubGroups ? this._addRowToGroup : this._addRow;
this.type = "group"; //type of element
this.old = oldGroup;
this.rows = [];
this.groups = [];
this.groupList = [];
this.generator = generator;
this.element = false;
this.elementContents = false;
this.height = 0;
this.outerHeight = 0;
this.initialized = false;
this.calcs = {};
this.initialized = false;
this.modules = {};
this.arrowElement = false;
this.visible = oldGroup ? oldGroup.visible : (typeof groupManager.startOpen[level] !== "undefined" ? groupManager.startOpen[level] : groupManager.startOpen[0]);
this.component = null;
this.createElements();
this.addBindings();
this.createValueGroups();
}
wipe(elementsOnly){
if(!elementsOnly){
if(this.groupList.length){
this.groupList.forEach(function(group){
group.wipe();
});
}else {
this.rows.forEach((row) => {
if(row.modules){
delete row.modules.group;
}
});
}
}
this.element = false;
this.arrowElement = false;
this.elementContents = false;
}
createElements(){
var arrow = document.createElement("div");
arrow.classList.add("tabulator-arrow");
this.element = document.createElement("div");
this.element.classList.add("tabulator-row");
this.element.classList.add("tabulator-group");
this.element.classList.add("tabulator-group-level-" + this.level);
this.element.setAttribute("role", "rowgroup");
this.arrowElement = document.createElement("div");
this.arrowElement.classList.add("tabulator-group-toggle");
this.arrowElement.appendChild(arrow);
//setup movable rows
if(this.groupManager.table.options.movableRows !== false && this.groupManager.table.modExists("moveRow")){
this.groupManager.table.modules.moveRow.initializeGroupHeader(this);
}
}
createValueGroups(){
var level = this.level + 1;
if(this.groupManager.allowedValues && this.groupManager.allowedValues[level]){
this.groupManager.allowedValues[level].forEach((value) => {
this._createGroup(value, level);
});
}
}
addBindings(){
var toggleElement;
if(this.groupManager.table.options.groupToggleElement){
toggleElement = this.groupManager.table.options.groupToggleElement == "arrow" ? this.arrowElement : this.element;
toggleElement.addEventListener("click", (e) => {
e.stopPropagation();
e.stopImmediatePropagation();
this.toggleVisibility();
});
}
}
_createGroup(groupID, level){
var groupKey = level + "_" + groupID;
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);
this.groups[groupKey] = group;
this.groupList.push(group);
}
_addRowToGroup(row){
var level = this.level + 1;
if(this.hasSubGroups){
var groupID = this.groupManager.groupIDLookups[level].func(row.getData()),
groupKey = level + "_" + groupID;
if(this.groupManager.allowedValues && this.groupManager.allowedValues[level]){
if(this.groups[groupKey]){
this.groups[groupKey].addRow(row);
}
}else {
if(!this.groups[groupKey]){
this._createGroup(groupID, level);
}
this.groups[groupKey].addRow(row);
}
}
}
_addRow(row){
this.rows.push(row);
row.modules.group = this;
}
insertRow(row, to, after){
var data = this.conformRowData({});
row.updateData(data);
var toIndex = this.rows.indexOf(to);
if(toIndex > -1){
if(after){
this.rows.splice(toIndex+1, 0, row);
}else {
this.rows.splice(toIndex, 0, row);
}
}else {
if(after){
this.rows.push(row);
}else {
this.rows.unshift(row);
}
}
row.modules.group = this;
// this.generateGroupHeaderContents();
if(this.groupManager.table.modExists("columnCalcs") && this.groupManager.table.options.columnCalcs != "table"){
this.groupManager.table.modules.columnCalcs.recalcGroup(this);
}
this.groupManager.updateGroupRows(true);
}
scrollHeader(left){
if(this.arrowElement){
this.arrowElement.style.marginLeft = left;
this.groupList.forEach(function(child){
child.scrollHeader(left);
});
}
}
getRowIndex(row){}
//update row data to match grouping constraints
conformRowData(data){
if(this.field){
data[this.field] = this.key;
}else {
console.warn("Data Conforming Error - Cannot conform row data to match new group as groupBy is a function");
}
if(this.parent){
data = this.parent.conformRowData(data);
}
return data;
}
removeRow(row){
var index = this.rows.indexOf(row);
var el = row.getElement();
if(index > -1){
this.rows.splice(index, 1);
}
if(!this.groupManager.table.options.groupValues && !this.rows.length){
if(this.parent){
this.parent.removeGroup(this);
}else {
this.groupManager.removeGroup(this);
}
this.groupManager.updateGroupRows(true);
}else {
if(el.parentNode){
el.parentNode.removeChild(el);
}
if(!this.groupManager.blockRedraw){
this.generateGroupHeaderContents();
if(this.groupManager.table.modExists("columnCalcs") && this.groupManager.table.options.columnCalcs != "table"){
this.groupManager.table.modules.columnCalcs.recalcGroup(this);
}
}
}
}
removeGroup(group){
var groupKey = group.level + "_" + group.key,
index;
if(this.groups[groupKey]){
delete this.groups[groupKey];
index = this.groupList.indexOf(group);
if(index > -1){
this.groupList.splice(index, 1);
}
if(!this.groupList.length){
if(this.parent){
this.parent.removeGroup(this);
}else {
this.groupManager.removeGroup(this);
}
}
}
}
getHeadersAndRows(){
var output = [];
output.push(this);
this._visSet();
if(this.calcs.top){
this.calcs.top.detachElement();
this.calcs.top.deleteCells();
}
if(this.calcs.bottom){
this.calcs.bottom.detachElement();
this.calcs.bottom.deleteCells();
}
if(this.visible){
if(this.groupList.length){
this.groupList.forEach(function(group){
output = output.concat(group.getHeadersAndRows());
});
}else {
if(this.groupManager.table.options.columnCalcs != "table" && this.groupManager.table.modExists("columnCalcs") && this.groupManager.table.modules.columnCalcs.hasTopCalcs()){
this.calcs.top = this.groupManager.table.modules.columnCalcs.generateTopRow(this.rows);
output.push(this.calcs.top);
}
output = output.concat(this.rows);
if(this.groupManager.table.options.columnCalcs != "table" && this.groupManager.table.modExists("columnCalcs") && this.groupManager.table.modules.columnCalcs.hasBottomCalcs()){
this.calcs.bottom = this.groupManager.table.modules.columnCalcs.generateBottomRow(this.rows);
output.push(this.calcs.bottom);
}
}
}else {
if(!this.groupList.length && this.groupManager.table.options.columnCalcs != "table"){
if(this.groupManager.table.modExists("columnCalcs")){
if(this.groupManager.table.modules.columnCalcs.hasTopCalcs()){
if(this.groupManager.table.options.groupClosedShowCalcs){
this.calcs.top = this.groupManager.table.modules.columnCalcs.generateTopRow(this.rows);
output.push(this.calcs.top);
}
}
if(this.groupManager.table.modules.columnCalcs.hasBottomCalcs()){
if(this.groupManager.table.options.groupClosedShowCalcs){
this.calcs.bottom = this.groupManager.table.modules.columnCalcs.generateBottomRow(this.rows);
output.push(this.calcs.bottom);
}
}
}
}
}
return output;
}
getData(visible, transform){
var output = [];
this._visSet();
if(!visible || (visible && this.visible)){
this.rows.forEach((row) => {
output.push(row.getData(transform || "data"));
});
}
return output;
}
getRowCount(){
var count = 0;
if(this.groupList.length){
this.groupList.forEach((group) => {
count += group.getRowCount();
});
}else {
count = this.rows.length;
}
return count;
}
toggleVisibility(){
if(this.visible){
this.hide();
}else {
this.show();
}
}
hide(){
this.visible = false;
if(this.groupManager.table.rowManager.getRenderMode() == "basic" && !this.groupManager.table.options.pagination){
this.element.classList.remove("tabulator-group-visible");
if(this.groupList.length){
this.groupList.forEach((group) => {
var rows = group.getHeadersAndRows();
rows.forEach((row) => {
row.detachElement();
});
});
}else {
this.rows.forEach((row) => {
var rowEl = row.getElement();
rowEl.parentNode.removeChild(rowEl);
});
}
this.groupManager.updateGroupRows(true);
}else {
this.groupManager.updateGroupRows(true);
}
this.groupManager.table.externalEvents.dispatch("groupVisibilityChanged", this.getComponent(), false);
}
show(){
this.visible = true;
if(this.groupManager.table.rowManager.getRenderMode() == "basic" && !this.groupManager.table.options.pagination){
this.element.classList.add("tabulator-group-visible");
var prev = this.generateElement();
if(this.groupList.length){
this.groupList.forEach((group) => {
var rows = group.getHeadersAndRows();
rows.forEach((row) => {
var rowEl = row.getElement();
prev.parentNode.insertBefore(rowEl, prev.nextSibling);
row.initialize();
prev = rowEl;
});
});
}else {
this.rows.forEach((row) => {
var rowEl = row.getElement();
prev.parentNode.insertBefore(rowEl, prev.nextSibling);
row.initialize();
prev = rowEl;
});
}
this.groupManager.updateGroupRows(true);
}else {
this.groupManager.updateGroupRows(true);
}
this.groupManager.table.externalEvents.dispatch("groupVisibilityChanged", this.getComponent(), true);
}
_visSet(){
var data = [];
if(typeof this.visible == "function"){
this.rows.forEach(function(row){
data.push(row.getData());
});
this.visible = this.visible(this.key, this.getRowCount(), data, this.getComponent());
}
}
getRowGroup(row){
var match = false;
if(this.groupList.length){
this.groupList.forEach(function(group){
var result = group.getRowGroup(row);
if(result){
match = result;
}
});
}else {
if(this.rows.find(function(item){
return item === row;
})){
match = this;
}
}
return match;
}
getSubGroups(component){
var output = [];
this.groupList.forEach(function(child){
output.push(component ? child.getComponent() : child);
});
return output;
}
getRows(component){
var output = [];
this.rows.forEach(function(row){
output.push(component ? row.getComponent() : row);
});
return output;
}
generateGroupHeaderContents(){
var data = [];
this.rows.forEach(function(row){
data.push(row.getData());
});
this.elementContents = this.generator(this.key, this.getRowCount(), data, this.getComponent());
while(this.element.firstChild) this.element.removeChild(this.element.firstChild);
if(typeof this.elementContents === "string"){
this.element.innerHTML = this.elementContents;
}else {
this.element.appendChild(this.elementContents);
}
this.element.insertBefore(this.arrowElement, this.element.firstChild);
}
getPath(path = []) {
path.unshift(this.key);
if(this.parent) {
this.parent.getPath(path);
}
return path;
}
////////////// Standard Row Functions //////////////
getElement(){
return this.elementContents ? this.element : this.generateElement();
}
generateElement(){
this.addBindings = false;
this._visSet();
if(this.visible){
this.element.classList.add("tabulator-group-visible");
}else {
this.element.classList.remove("tabulator-group-visible");
}
for(var i = 0; i < this.element.childNodes.length; ++i){
this.element.childNodes[i].parentNode.removeChild(this.element.childNodes[i]);
}
this.generateGroupHeaderContents();
// this.addBindings();
return this.element;
}
detachElement(){
if (this.element && this.element.parentNode){
this.element.parentNode.removeChild(this.element);
}
}
//normalize the height of elements in the row
normalizeHeight(){
this.setHeight(this.element.clientHeight);
}
initialize(force){
if(!this.initialized || force){
this.normalizeHeight();
this.initialized = true;
}
}
reinitialize(){
this.initialized = false;
this.height = 0;
if(Helpers.elVisible(this.element)){
this.initialize(true);
}
}
setHeight(height){
if(this.height != height){
this.height = height;
this.outerHeight = this.element.offsetHeight;
}
}
//return rows outer height
getHeight(){
return this.outerHeight;
}
getGroup(){
return this;
}
reinitializeHeight(){}
calcHeight(){}
setCellHeight(){}
clearCellHeight(){}
deinitializeHeight(){}
//////////////// Object Generation /////////////////
getComponent(){
if(!this.component){
this.component = new GroupComponent(this);
}
return this.component;
}
}
class GroupRows extends Module{
constructor(table){
super(table);
this.groupIDLookups = false; //enable table grouping and set field to group by
this.startOpen = [function(){return false;}]; //starting state of group
this.headerGenerator = [function(){return "";}];
this.groupList = []; //ordered list of groups
this.allowedValues = false;
this.groups = {}; //hold row groups
this.displayHandler = this.getRows.bind(this);
this.blockRedraw = false;
//register table options
this.registerTableOption("groupBy", false); //enable table grouping and set field to group by
this.registerTableOption("groupStartOpen", true); //starting state of group
this.registerTableOption("groupValues", false);
this.registerTableOption("groupUpdateOnCellEdit", false);
this.registerTableOption("groupHeader", false); //header generation function
this.registerTableOption("groupHeaderPrint", null);
this.registerTableOption("groupHeaderClipboard", null);
this.registerTableOption("groupHeaderHtmlOutput", null);
this.registerTableOption("groupHeaderDownload", null);
this.registerTableOption("groupToggleElement", "arrow");
this.registerTableOption("groupClosedShowCalcs", false);
//register table functions
this.registerTableFunction("setGroupBy", this.setGroupBy.bind(this));
this.registerTableFunction("setGroupValues", this.setGroupValues.bind(this));
this.registerTableFunction("setGroupStartOpen", this.setGroupStartOpen.bind(this));
this.registerTableFunction("setGroupHeader", this.setGroupHeader.bind(this));
this.registerTableFunction("getGroups", this.userGetGroups.bind(this));
this.registerTableFunction("getGroupedData", this.userGetGroupedData.bind(this));
//register component functions
this.registerComponentFunction("row", "getGroup", this.rowGetGroup.bind(this));
}
//initialize group configuration
initialize(){
this.subscribe("table-destroy", this._blockRedrawing.bind(this));
this.subscribe("rows-wipe", this._blockRedrawing.bind(this));
this.subscribe("rows-wiped", this._restore_redrawing.bind(this));
if(this.table.options.groupBy){
if(this.table.options.groupUpdateOnCellEdit){
this.subscribe("cell-value-updated", this.cellUpdated.bind(this));
this.subscribe("row-data-changed", this.reassignRowToGroup.bind(this), 0);
}
this.subscribe("table-built", this.configureGroupSetup.bind(this));
this.subscribe("row-deleting", this.rowDeleting.bind(this));
this.subscribe("row-deleted", this.rowsUpdated.bind(this));
this.subscribe("scroll-horizontal", this.scrollHeaders.bind(this));
this.subscribe("rows-wipe", this.wipe.bind(this));
this.subscribe("rows-added", this.rowsUpdated.bind(this));
this.subscribe("row-moving", this.rowMoving.bind(this));
this.subscribe("row-adding-index", this.rowAddingIndex.bind(this));
this.subscribe("rows-sample", this.rowSample.bind(this));
this.subscribe("render-virtual-fill", this.virtualRenderFill.bind(this));
this.registerDisplayHandler(this.displayHandler, 20);
this.initialized = true;
}
}
_blockRedrawing(){
this.blockRedraw = true;
}
_restore_redrawing(){
this.blockRedraw = false;
}
configureGroupSetup(){
if(this.table.options.groupBy){
var groupBy = this.table.options.groupBy,
startOpen = this.table.options.groupStartOpen,
groupHeader = this.table.options.groupHeader;
this.allowedValues = this.table.options.groupValues;
if(Array.isArray(groupBy) && Array.isArray(groupHeader) && groupBy.length > groupHeader.length){
console.warn("Error creating group headers, groupHeader array is shorter than groupBy array");
}
this.headerGenerator = [function(){return "";}];
this.startOpen = [function(){return false;}]; //starting state of group
this.langBind("groups|item", (langValue, lang) => {
this.headerGenerator[0] = (value, count, data) => { //header layout function
return (typeof value === "undefined" ? "" : value) + "(" + count + " " + ((count === 1) ? langValue : lang.groups.items) + ")";
};
});
this.groupIDLookups = [];
if(groupBy){
if(this.table.modExists("columnCalcs") && this.table.options.columnCalcs != "table" && this.table.options.columnCalcs != "both"){
this.table.modules.columnCalcs.removeCalcs();
}
}else {
if(this.table.modExists("columnCalcs") && this.table.options.columnCalcs != "group"){
var cols = this.table.columnManager.getRealColumns();
cols.forEach((col) => {
if(col.definition.topCalc){
this.table.modules.columnCalcs.initializeTopRow();
}
if(col.definition.bottomCalc){
this.table.modules.columnCalcs.initializeBottomRow();
}
});
}
}
if(!Array.isArray(groupBy)){
groupBy = [groupBy];
}
groupBy.forEach((group, i) => {
var lookupFunc, column;
if(typeof group == "function"){
lookupFunc = group;
}else {
column = this.table.columnManager.getColumnByField(group);
if(column){
lookupFunc = function(data){
return column.getFieldValue(data);
};
}else {
lookupFunc = function(data){
return data[group];
};
}
}
this.groupIDLookups.push({
field: typeof group === "function" ? false : group,
func:lookupFunc,
values:this.allowedValues ? this.allowedValues[i] : false,
});
});
if(startOpen){
if(!Array.isArray(startOpen)){
startOpen = [startOpen];
}
startOpen.forEach((level) => {
});
this.startOpen = startOpen;
}
if(groupHeader){
this.headerGenerator = Array.isArray(groupHeader) ? groupHeader : [groupHeader];
}
}else {
this.groupList = [];
this.groups = {};
}
}
rowSample(rows, prevValue){
if(this.table.options.groupBy){
var group = this.getGroups(false)[0];
prevValue.push(group.getRows(false)[0]);
}
return prevValue;
}
virtualRenderFill(){
var el = this.table.rowManager.tableElement;
var rows = this.table.rowManager.getVisibleRows();
if(this.table.options.groupBy){
rows = rows.filter((row) => {
return row.type !== "group";
});
el.style.minWidth = !rows.length ? this.table.columnManager.getWidth() + "px" : "";
}else {
return rows;
}
}
rowAddingIndex(row, index, top){
if(this.table.options.groupBy){
this.assignRowToGroup(row);
var groupRows = row.modules.group.rows;
if(groupRows.length > 1){
if(!index || (index && groupRows.indexOf(index) == -1)){
if(top){
if(groupRows[0] !== row){
index = groupRows[0];
this.table.rowManager.moveRowInArray(row.modules.group.rows, row, index, !top);
}
}else {
if(groupRows[groupRows.length -1] !== row){
index = groupRows[groupRows.length -1];
this.table.rowManager.moveRowInArray(row.modules.group.rows, row, index, !top);
}
}
}else {
this.table.rowManager.moveRowInArray(row.modules.group.rows, row, index, !top);
}
}
return index;
}
}
trackChanges(){
this.dispatch("group-changed");
}
///////////////////////////////////
///////// Table Functions /////////
///////////////////////////////////
setGroupBy(groups){
this.table.options.groupBy = groups;
if(!this.initialized){
this.initialize();
}
this.configureGroupSetup();
if(!groups && this.table.modExists("columnCalcs") && this.table.options.columnCalcs === true){
this.table.modules.columnCalcs.reinitializeCalcs();
}
this.refreshData();
this.trackChanges();
}
setGroupValues(groupValues){
this.table.options.groupValues = groupValues;
this.configureGroupSetup();
this.refreshData();
this.trackChanges();
}
setGroupStartOpen(values){
this.table.options.groupStartOpen = values;
this.configureGroupSetup();
if(this.table.options.groupBy){
this.refreshData();
this.trackChanges();
}else {
console.warn("Grouping Update - cant refresh view, no groups have been set");
}
}
setGroupHeader(values){
this.table.options.groupHeader = values;
this.configureGroupSetup();
if(this.table.options.groupBy){
this.refreshData();
this.trackChanges();
}else {
console.warn("Grouping Update - cant refresh view, no groups have been set");
}
}
userGetGroups(values){
return this.getGroups(true);
}
// get grouped table data in the same format as getData()
userGetGroupedData(){
return this.table.options.groupBy ? this.getGroupedData() : this.getData();
}
///////////////////////////////////////
///////// Component Functions /////////
///////////////////////////////////////
rowGetGroup(row){
return row.modules.group ? row.modules.group.getComponent() : false;
}
///////////////////////////////////
///////// Internal Logic //////////
///////////////////////////////////
rowMoving(from, to, after){
if(this.table.options.groupBy){
if(!after && to instanceof Group){
to = this.table.rowManager.prevDisplayRow(from) || to;
}
var toGroup = to instanceof Group ? to : to.modules.group;
var fromGroup = from instanceof Group ? from : from.modules.group;
if(toGroup === fromGroup){
this.table.rowManager.moveRowInArray(toGroup.rows, from, to, after);
}else {
if(fromGroup){
fromGroup.removeRow(from);
}
toGroup.insertRow(from, to, after);
}
}
}
rowDeleting(row){
//remove from group
if(this.table.options.groupBy && row.modules.group){
row.modules.group.removeRow(row);
}
}
rowsUpdated(row){
if(this.table.options.groupBy){
this.updateGroupRows(true);
}
}
cellUpdated(cell){
if(this.table.options.groupBy){
this.reassignRowToGroup(cell.row);
}
}
//return appropriate rows with group headers
getRows(rows){
if(this.table.options.groupBy && this.groupIDLookups.length){
this.dispatchExternal("dataGrouping");
this.generateGroups(rows);
if(this.subscribedExternal("dataGrouped")){
this.dispatchExternal("dataGrouped", this.getGroups(true));
}
return this.updateGroupRows();
}else {
return rows.slice(0);
}
}
getGroups(component){
var groupComponents = [];
this.groupList.forEach(function(group){
groupComponents.push(component ? group.getComponent() : group);
});
return groupComponents;
}
getChildGroups(group){
var groupComponents = [];
if(!group){
group = this;
}
group.groupList.forEach((child) => {
if(child.groupList.length){
groupComponents = groupComponents.concat(this.getChildGroups(child));
}else {
groupComponents.push(child);
}
});
return groupComponents;
}
wipe(){
if(this.table.options.groupBy){
this.groupList.forEach(function(group){
group.wipe();
});
this.groupList = [];
this.groups = {};
}
}
pullGroupListData(groupList) {
var groupListData = [];
groupList.forEach((group) => {
var groupHeader = {};
groupHeader.level = 0;
groupHeader.rowCount = 0;
groupHeader.headerContent = "";
var childData = [];
if (group.hasSubGroups) {
childData = this.pullGroupListData(group.groupList);
groupHeader.level = group.level;
groupHeader.rowCount = childData.length - group.groupList.length; // data length minus number of sub-headers
groupHeader.headerContent = group.generator(group.key, groupHeader.rowCount, group.rows, group);
groupListData.push(groupHeader);
groupListData = groupListData.concat(childData);
}
else {
groupHeader.level = group.level;
groupHeader.headerContent = group.generator(group.key, group.rows.length, group.rows, group);
groupHeader.rowCount = group.getRows().length;
groupListData.push(groupHeader);
group.getRows().forEach((row) => {
groupListData.push(row.getData("data"));
});
}
});
return groupListData;
}
getGroupedData(){
return this.pullGroupListData(this.groupList);
}
getRowGroup(row){
var match = false;
if(this.options("dataTree")){
row = this.table.modules.dataTree.getTreeParentRoot(row);
}
this.groupList.forEach((group) => {
var result = group.getRowGroup(row);
if(result){
match = result;
}
});
return match;
}
countGroups(){
return this.groupList.length;
}
generateGroups(rows){
var oldGroups = this.groups;
this.groups = {};
this.groupList = [];
if(this.allowedValues && this.allowedValues[0]){
this.allowedValues[0].forEach((value) => {
this.createGroup(value, 0, oldGroups);
});
rows.forEach((row) => {
this.assignRowToExistingGroup(row, oldGroups);
});
}else {
rows.forEach((row) => {
this.assignRowToGroup(row, oldGroups);
});
}
Object.values(oldGroups).forEach((group) => {
group.wipe(true);
});
}
createGroup(groupID, level, oldGroups){
var groupKey = level + "_" + groupID,
group;
oldGroups = oldGroups || [];
group = new Group(this, false, level, groupID, this.groupIDLookups[0].field, this.headerGenerator[0], oldGroups[groupKey]);
this.groups[groupKey] = group;
this.groupList.push(group);
}
assignRowToExistingGroup(row, oldGroups){
var groupID = this.groupIDLookups[0].func(row.getData()),
groupKey = "0_" + groupID;
if(this.groups[groupKey]){
this.groups[groupKey].addRow(row);
}
}
assignRowToGroup(row, oldGroups){
var groupID = this.groupIDLookups[0].func(row.getData()),
newGroupNeeded = !this.groups["0_" + groupID];
if(newGroupNeeded){
this.createGroup(groupID, 0, oldGroups);
}
this.groups["0_" + groupID].addRow(row);
return !newGroupNeeded;
}
reassignRowToGroup(row){
if(row.type === "row"){
var oldRowGroup = row.modules.group,
oldGroupPath = oldRowGroup.getPath(),
newGroupPath = this.getExpectedPath(row),
samePath;
// figure out if new group path is the same as old group path
samePath = (oldGroupPath.length == newGroupPath.length) && oldGroupPath.every((element, index) => {
return element === newGroupPath[index];
});
// refresh if they new path and old path aren't the same (aka the row's groupings have changed)
if(!samePath) {
oldRowGroup.removeRow(row);
this.assignRowToGroup(row, this.groups);
this.refreshData(true);
}
}
}
getExpectedPath(row) {
var groupPath = [], rowData = row.getData();
this.groupIDLookups.forEach((groupId) => {
groupPath.push(groupId.func(rowData));
});
return groupPath;
}
updateGroupRows(force){
var output = [];
if(!this.blockRedraw){
this.groupList.forEach((group) => {
output = output.concat(group.getHeadersAndRows());
});
if(force){
this.refreshData(true);
}
}
return output;
}
scrollHeaders(left){
if(this.table.options.groupBy){
if(this.table.options.renderHorizontal === "virtual"){
left -= this.table.columnManager.renderer.vDomPadLeft;
}
left = left + "px";
this.groupList.forEach((group) => {
group.scrollHeader(left);
});
}
}
removeGroup(group){
var groupKey = group.level + "_" + group.key,
index;
if(this.groups[groupKey]){
delete this.groups[groupKey];
index = this.groupList.indexOf(group);
if(index > -1){
this.groupList.splice(index, 1);
}
}
}
checkBasicModeGroupHeaderWidth(){
var element = this.table.rowManager.tableElement,
onlyGroupHeaders = true;
this.table.rowManager.getDisplayRows().forEach((row, index) =>{
this.table.rowManager.styleRow(row, index);
element.appendChild(row.getElement());
row.initialize(true);
if(row.type !== "group"){
onlyGroupHeaders = false;
}
});
if(onlyGroupHeaders){
element.style.minWidth = this.table.columnManager.getWidth() + "px";
}else {
element.style.minWidth = "";
}
}
}
GroupRows.moduleName = "groupRows";
var defaultUndoers = {
cellEdit: function(action){
action.component.setValueProcessData(action.data.oldValue);
action.component.cellRendered();
},
rowAdd: function(action){
action.component.deleteActual();
},
rowDelete: function(action){
var newRow = this.table.rowManager.addRowActual(action.data.data, action.data.pos, action.data.index);
if(this.table.options.groupBy && this.table.modExists("groupRows")){
this.table.modules.groupRows.updateGroupRows(true);
}
this._rebindRow(action.component, newRow);
},
rowMove: function(action){
this.table.rowManager.moveRowActual(action.component, this.table.rowManager.rows[action.data.posFrom], !action.data.after);
this.table.rowManager.redraw();
},
};
var defaultRedoers = {
cellEdit: function(action){
action.component.setValueProcessData(action.data.newValue);
action.component.cellRendered();
},
rowAdd: function(action){
var newRow = this.table.rowManager.addRowActual(action.data.data, action.data.pos, action.data.index);
if(this.table.options.groupBy && this.table.modExists("groupRows")){
this.table.modules.groupRows.updateGroupRows(true);
}
this._rebindRow(action.component, newRow);
},
rowDelete:function(action){
action.component.deleteActual();
},
rowMove: function(action){
this.table.rowManager.moveRowActual(action.component, this.table.rowManager.rows[action.data.posTo], action.data.after);
this.table.rowManager.redraw();
},
};
class History extends Module{
constructor(table){
super(table);
this.history = [];
this.index = -1;
this.registerTableOption("history", false); //enable edit history
}
initialize(){
if(this.table.options.history){
this.subscribe("cell-value-updated", this.cellUpdated.bind(this));
this.subscribe("cell-delete", this.clearComponentHistory.bind(this));
this.subscribe("row-delete", this.rowDeleted.bind(this));
this.subscribe("rows-wipe", this.clear.bind(this));
this.subscribe("row-added", this.rowAdded.bind(this));
this.subscribe("row-move", this.rowMoved.bind(this));
}
this.registerTableFunction("undo", this.undo.bind(this));
this.registerTableFunction("redo", this.redo.bind(this));
this.registerTableFunction("getHistoryUndoSize", this.getHistoryUndoSize.bind(this));
this.registerTableFunction("getHistoryRedoSize", this.getHistoryRedoSize.bind(this));
this.registerTableFunction("clearHistory", this.clear.bind(this));
}
rowMoved(from, to, after){
this.action("rowMove", from, {posFrom:from.getPosition(), posTo:to.getPosition(), to:to, after:after});
}
rowAdded(row, data, pos, index){
this.action("rowAdd", row, {data:data, pos:pos, index:index});
}
rowDeleted(row){
var index, rows;
if(this.table.options.groupBy){
rows = row.getComponent().getGroup()._getSelf().rows;
index = rows.indexOf(row);
if(index){
index = rows[index-1];
}
}else {
index = row.table.rowManager.getRowIndex(row);
if(index){
index = row.table.rowManager.rows[index-1];
}
}
this.action("rowDelete", row, {data:row.getData(), pos:!index, index:index});
}
cellUpdated(cell){
this.action("cellEdit", cell, {oldValue:cell.oldValue, newValue:cell.value});
}
clear(){
this.history = [];
this.index = -1;
}
action(type, component, data){
this.history = this.history.slice(0, this.index + 1);
this.history.push({
type:type,
component:component,
data:data,
});
this.index ++;
}
getHistoryUndoSize(){
return this.index + 1;
}
getHistoryRedoSize(){
return this.history.length - (this.index + 1);
}
clearComponentHistory(component){
var index = this.history.findIndex(function(item){
return item.component === component;
});
if(index > -1){
this.history.splice(index, 1);
if(index <= this.index){
this.index--;
}
this.clearComponentHistory(component);
}
}
undo(){
if(this.index > -1){
let action = this.history[this.index];
History.undoers[action.type].call(this, action);
this.index--;
this.dispatchExternal("historyUndo", action.type, action.component.getComponent(), action.data);
return true;
}else {
console.warn("History Undo Error - No more history to undo");
return false;
}
}
redo(){
if(this.history.length-1 > this.index){
this.index++;
let action = this.history[this.index];
History.redoers[action.type].call(this, action);
this.dispatchExternal("historyRedo", action.type, action.component.getComponent(), action.data);
return true;
}else {
console.warn("History Redo Error - No more history to redo");
return false;
}
}
//rebind rows to new element after deletion
_rebindRow(oldRow, newRow){
this.history.forEach(function(action){
if(action.component instanceof Row){
if(action.component === oldRow){
action.component = newRow;
}
}else if(action.component instanceof Cell){
if(action.component.row === oldRow){
var field = action.component.column.getField();
if(field){
action.component = newRow.getCell(field);
}
}
}
});
}
}
History.moduleName = "history";
//load defaults
History.undoers = defaultUndoers;
History.redoers = defaultRedoers;
class HtmlTableImport extends Module{
constructor(table){
super(table);
this.fieldIndex = [];
this.hasIndex = false;
}
initialize(){
this.tableElementCheck();
}
tableElementCheck(){
if(this.table.originalElement && this.table.originalElement.tagName === "TABLE"){
if(this.table.originalElement.childNodes.length){
this.parseTable();
}else {
console.warn("Unable to parse data from empty table tag, Tabulator should be initialized on a div tag unless importing data from a table element.");
}
}
}
parseTable(){
var element = this.table.originalElement,
options = this.table.options,
headers = element.getElementsByTagName("th"),
rows = element.getElementsByTagName("tbody")[0],
data = [];
this.hasIndex = false;
this.dispatchExternal("htmlImporting");
rows = rows ? rows.getElementsByTagName("tr") : [];
//check for Tabulator inline options
this._extractOptions(element, options);
if(headers.length){
this._extractHeaders(headers, rows);
}else {
this._generateBlankHeaders(headers, rows);
}
//iterate through table rows and build data set
for(var index = 0; index < rows.length; index++){
var row = rows[index],
cells = row.getElementsByTagName("td"),
item = {};
//create index if the don't exist in table
if(!this.hasIndex){
item[options.index] = index;
}
for(var i = 0; i < cells.length; i++){
var cell = cells[i];
if(typeof this.fieldIndex[i] !== "undefined"){
item[this.fieldIndex[i]] = cell.innerHTML;
}
}
//add row data to item
data.push(item);
}
options.data = data;
this.dispatchExternal("htmlImported");
}
//extract tabulator attribute options
_extractOptions(element, options, defaultOptions){
var attributes = element.attributes;
var optionsArr = defaultOptions ? Object.keys(defaultOptions) : Object.keys(options);
var optionsList = {};
optionsArr.forEach((item) => {
optionsList[item.toLowerCase()] = item;
});
for(var index in attributes){
var attrib = attributes[index];
var name;
if(attrib && typeof attrib == "object" && attrib.name && attrib.name.indexOf("tabulator-") === 0){
name = attrib.name.replace("tabulator-", "");
if(typeof optionsList[name] !== "undefined"){
options[optionsList[name]] = this._attribValue(attrib.value);
}
}
}
}
//get value of attribute
_attribValue(value){
if(value === "true"){
return true;
}
if(value === "false"){
return false;
}
return value;
}
//find column if it has already been defined
_findCol(title){
var match = this.table.options.columns.find((column) => {
return column.title === title;
});
return match || false;
}
//extract column from headers
_extractHeaders(headers, rows){
for(var index = 0; index < headers.length; index++){
var header = headers[index],
exists = false,
col = this._findCol(header.textContent),
width;
if(col){
exists = true;
}else {
col = {title:header.textContent.trim()};
}
if(!col.field) {
col.field = header.textContent.trim().toLowerCase().replace(" ", "_");
}
width = header.getAttribute("width");
if(width && !col.width) {
col.width = width;
}
//check for Tabulator inline options
this._extractOptions(header, col, this.table.columnManager.optionsList.registeredDefaults);
this.fieldIndex[index] = col.field;
if(col.field == this.table.options.index){
this.hasIndex = true;
}
if(!exists){
this.table.options.columns.push(col);
}
}
}
//generate blank headers
_generateBlankHeaders(headers, rows){
for(var index = 0; index < headers.length; index++){
var header = headers[index],
col = {title:"", field:"col" + index};
this.fieldIndex[index] = col.field;
var width = header.getAttribute("width");
if(width){
col.width = width;
}
this.table.options.columns.push(col);
}
}
}
HtmlTableImport.moduleName = "htmlTableImport";
function csvImporter(input){
var data = [],
row = 0,
col = 0,
inQuote = false;
//Iterate over each character
for (let index = 0; index < input.length; index++) {
let char = input[index],
nextChar = input[index+1];
//Initialize empty row
if(!data[row]){
data[row] = [];
}
//Initialize empty column
if(!data[row][col]){
data[row][col] = "";
}
//Handle quotation mark inside string
if (char == '"' && inQuote && nextChar == '"') {
data[row][col] += char;
index++;
continue;
}
//Begin / End Quote
if (char == '"') {
inQuote = !inQuote;
continue;
}
//Next column (if not in quote)
if (char == ',' && !inQuote) {
col++;
continue;
}
//New row if new line and not in quote (CRLF)
if (char == '\r' && nextChar == '\n' && !inQuote) {
col = 0;
row++;
index++;
continue;
}
//New row if new line and not in quote (CR or LF)
if ((char == '\r' || char == '\n') && !inQuote) {
col = 0;
row++;
continue;
}
//Normal Character, append to column
data[row][col] += char;
}
return data;
}
function json$1(input){
try {
return JSON.parse(input);
} catch(e) {
console.warn("JSON Import Error - File contents is invalid JSON", e);
return Promise.reject();
}
}
function arrayImporter(input){
return input;
}
var defaultImporters = {
csv:csvImporter,
json:json$1,
array:arrayImporter,
};
class Import extends Module{
constructor(table){
super(table);
this.registerTableOption("importFormat");
this.registerTableOption("importReader", "text");
}
initialize(){
this.registerTableFunction("import", this.importFromFile.bind(this));
if(this.table.options.importFormat){
this.subscribe("data-loading", this.loadDataCheck.bind(this), 10);
this.subscribe("data-load", this.loadData.bind(this), 10);
}
}
loadDataCheck(data){
return this.table.options.importFormat && (typeof data === "string" || (Array.isArray(data) && data.length && Array.isArray(data)));
}
loadData(data, params, config, silent, previousData){
return this.importData(this.lookupImporter(), data)
.then(this.structureData.bind(this))
.catch((err) => {
console.error("Import Error:", err || "Unable to import data");
return Promise.reject(err);
});
}
lookupImporter(importFormat){
var importer;
if(!importFormat){
importFormat = this.table.options.importFormat;
}
if(typeof importFormat === "string"){
importer = Import.importers[importFormat];
}else {
importer = importFormat;
}
if(!importer){
console.error("Import Error - Importer not found:", importFormat);
}
return importer;
}
importFromFile(importFormat, extension){
var importer = this.lookupImporter(importFormat);
if(importer){
return this.pickFile(extension)
.then(this.importData.bind(this, importer))
.then(this.structureData.bind(this))
.then(this.setData.bind(this))
.catch((err) => {
console.error("Import Error:", err || "Unable to import file");
return Promise.reject(err);
});
}
}
pickFile(extensions){
return new Promise((resolve, reject) => {
var input = document.createElement("input");
input.type = "file";
input.accept = extensions;
input.addEventListener("change", (e) => {
var file = input.files[0],
reader = new FileReader();
switch(this.table.options.importReader){
case "buffer":
reader.readAsArrayBuffer(file);
break;
case "binary":
reader.readAsBinaryString(file);
break;
case "url":
reader.readAsDataURL(file);
break;
case "text":
default:
reader.readAsText(file);
}
reader.onload = (e) => {
resolve(reader.result);
};
reader.onerror = (e) => {
console.warn("File Load Error - Unable to read file");
reject();
};
});
input.click();
});
}
importData(importer, fileContents){
var data = importer.call(this.table, fileContents);
if(data instanceof Promise){
return data;
}else {
return data ? Promise.resolve(data) : Promise.reject();
}
}
structureData(parsedData){
var data = [];
if(Array.isArray(parsedData) && parsedData.length && Array.isArray(parsedData[0])){
if(this.table.options.autoColumns){
data = this.structureArrayToObject(parsedData);
}else {
data = this.structureArrayToColumns(parsedData);
}
return data;
}else {
return parsedData;
}
}
structureArrayToObject(parsedData){
var columns = parsedData.shift();
var data = parsedData.map((values) => {
var row = {};
columns.forEach((key, i) => {
row[key] = values[i];
});
return row;
});
return data;
}
structureArrayToColumns(parsedData){
var data = [],
columns = this.table.getColumns();
//remove first row if it is the column names
if(columns[0] && parsedData[0][0]){
if(columns[0].getDefinition().title === parsedData[0][0]){
parsedData.shift();
}
}
//convert row arrays to objects
parsedData.forEach((rowData) => {
var row = {};
rowData.forEach((value, index) => {
var column = columns[index];
if(column){
row[column.getField()] = value;
}
});
data.push(row);
});
return data;
}
setData(data){
return this.table.setData(data);
}
}
Import.moduleName = "import";
//load defaults
Import.importers = defaultImporters;
class Interaction extends Module{
constructor(table){
super(table);
this.eventMap = {
//row events
rowClick:"row-click",
rowDblClick:"row-dblclick",
rowContext:"row-contextmenu",
rowMouseEnter:"row-mouseenter",
rowMouseLeave:"row-mouseleave",
rowMouseOver:"row-mouseover",
rowMouseOut:"row-mouseout",
rowMouseMove:"row-mousemove",
rowMouseDown:"row-mousedown",
rowMouseUp:"row-mouseup",
rowTap:"row",
rowDblTap:"row",
rowTapHold:"row",
//cell events
cellClick:"cell-click",
cellDblClick:"cell-dblclick",
cellContext:"cell-contextmenu",
cellMouseEnter:"cell-mouseenter",
cellMouseLeave:"cell-mouseleave",
cellMouseOver:"cell-mouseover",
cellMouseOut:"cell-mouseout",
cellMouseMove:"cell-mousemove",
cellMouseDown:"cell-mousedown",
cellMouseUp:"cell-mouseup",
cellTap:"cell",
cellDblTap:"cell",
cellTapHold:"cell",
//column header events
headerClick:"column-click",
headerDblClick:"column-dblclick",
headerContext:"column-contextmenu",
headerMouseEnter:"column-mouseenter",
headerMouseLeave:"column-mouseleave",
headerMouseOver:"column-mouseover",
headerMouseOut:"column-mouseout",
headerMouseMove:"column-mousemove",
headerMouseDown:"column-mousedown",
headerMouseUp:"column-mouseup",
headerTap:"column",
headerDblTap:"column",
headerTapHold:"column",
//group header
groupClick:"group-click",
groupDblClick:"group-dblclick",
groupContext:"group-contextmenu",
groupMouseEnter:"group-mouseenter",
groupMouseLeave:"group-mouseleave",
groupMouseOver:"group-mouseover",
groupMouseOut:"group-mouseout",
groupMouseMove:"group-mousemove",
groupMouseDown:"group-mousedown",
groupMouseUp:"group-mouseup",
groupTap:"group",
groupDblTap:"group",
groupTapHold:"group",
};
this.subscribers = {};
this.touchSubscribers = {};
this.columnSubscribers = {};
this.touchWatchers = {
row:{
tap:null,
tapDbl:null,
tapHold:null,
},
cell:{
tap:null,
tapDbl:null,
tapHold:null,
},
column:{
tap:null,
tapDbl:null,
tapHold:null,
},
group:{
tap:null,
tapDbl:null,
tapHold:null,
}
};
this.registerColumnOption("headerClick");
this.registerColumnOption("headerDblClick");
this.registerColumnOption("headerContext");
this.registerColumnOption("headerMouseEnter");
this.registerColumnOption("headerMouseLeave");
this.registerColumnOption("headerMouseOver");
this.registerColumnOption("headerMouseOut");
this.registerColumnOption("headerMouseMove");
this.registerColumnOption("headerMouseDown");
this.registerColumnOption("headerMouseUp");
this.registerColumnOption("headerTap");
this.registerColumnOption("headerDblTap");
this.registerColumnOption("headerTapHold");
this.registerColumnOption("cellClick");
this.registerColumnOption("cellDblClick");
this.registerColumnOption("cellContext");
this.registerColumnOption("cellMouseEnter");
this.registerColumnOption("cellMouseLeave");
this.registerColumnOption("cellMouseOver");
this.registerColumnOption("cellMouseOut");
this.registerColumnOption("cellMouseMove");
this.registerColumnOption("cellMouseDown");
this.registerColumnOption("cellMouseUp");
this.registerColumnOption("cellTap");
this.registerColumnOption("cellDblTap");
this.registerColumnOption("cellTapHold");
}
initialize(){
this.initializeExternalEvents();
this.subscribe("column-init", this.initializeColumn.bind(this));
this.subscribe("cell-dblclick", this.cellContentsSelectionFixer.bind(this));
}
cellContentsSelectionFixer(e, cell){
var range;
if(this.table.modExists("edit")){
if (this.table.modules.edit.currentCell === cell){
return; //prevent instant selection of editor content
}
}
e.preventDefault();
try{
if (document.selection) { // IE
range = document.body.createTextRange();
range.moveToElementText(cell.getElement());
range.select();
} else if (window.getSelection) {
range = document.createRange();
range.selectNode(cell.getElement());
window.getSelection().removeAllRanges();
window.getSelection().addRange(range);
}
}catch(e){}
}
initializeExternalEvents(){
for(let key in this.eventMap){
this.subscriptionChangeExternal(key, this.subscriptionChanged.bind(this, key));
}
}
subscriptionChanged(key, added){
if(added){
if(!this.subscribers[key]){
if(this.eventMap[key].includes("-")){
this.subscribers[key] = this.handle.bind(this, key);
this.subscribe(this.eventMap[key], this.subscribers[key]);
}else {
this.subscribeTouchEvents(key);
}
}
}else {
if(this.eventMap[key].includes("-")){
if(this.subscribers[key] && !this.columnSubscribers[key] && !this.subscribedExternal(key)){
this.unsubscribe(this.eventMap[key], this.subscribers[key]);
delete this.subscribers[key];
}
}else {
this.unsubscribeTouchEvents(key);
}
}
}
subscribeTouchEvents(key){
var type = this.eventMap[key];
if(!this.touchSubscribers[type + "-touchstart"]){
this.touchSubscribers[type + "-touchstart"] = this.handleTouch.bind(this, type, "start");
this.touchSubscribers[type + "-touchend"] = this.handleTouch.bind(this, type, "end");
this.subscribe(type + "-touchstart", this.touchSubscribers[type + "-touchstart"]);
this.subscribe(type + "-touchend", this.touchSubscribers[type + "-touchend"]);
}
this.subscribers[key] = true;
}
unsubscribeTouchEvents(key){
var noTouch = true,
type = this.eventMap[key];
if(this.subscribers[key] && !this.subscribedExternal(key)){
delete this.subscribers[key];
for(let i in this.eventMap){
if(this.eventMap[i] === type){
if(this.subscribers[i]){
noTouch = false;
}
}
}
if(noTouch){
this.unsubscribe(type + "-touchstart", this.touchSubscribers[type + "-touchstart"]);
this.unsubscribe(type + "-touchend", this.touchSubscribers[type + "-touchend"]);
delete this.touchSubscribers[type + "-touchstart"];
delete this.touchSubscribers[type + "-touchend"];
}
}
}
initializeColumn(column){
var def = column.definition;
for(let key in this.eventMap){
if(def[key]){
this.subscriptionChanged(key, true);
if(!this.columnSubscribers[key]){
this.columnSubscribers[key] = [];
}
this.columnSubscribers[key].push(column);
}
}
}
handle(action, e, component){
this.dispatchEvent(action, e, component);
}
handleTouch(type, action, e, component){
var watchers = this.touchWatchers[type];
if(type === "column"){
type = "header";
}
switch(action){
case "start":
watchers.tap = true;
clearTimeout(watchers.tapHold);
watchers.tapHold = setTimeout(() => {
clearTimeout(watchers.tapHold);
watchers.tapHold = null;
watchers.tap = null;
clearTimeout(watchers.tapDbl);
watchers.tapDbl = null;
this.dispatchEvent(type + "TapHold", e, component);
}, 1000);
break;
case "end":
if(watchers.tap){
watchers.tap = null;
this.dispatchEvent(type + "Tap", e, component);
}
if(watchers.tapDbl){
clearTimeout(watchers.tapDbl);
watchers.tapDbl = null;
this.dispatchEvent(type + "DblTap", e, component);
}else {
watchers.tapDbl = setTimeout(() => {
clearTimeout(watchers.tapDbl);
watchers.tapDbl = null;
}, 300);
}
clearTimeout(watchers.tapHold);
watchers.tapHold = null;
break;
}
}
dispatchEvent(action, e, component){
var componentObj = component.getComponent(),
callback;
if(this.columnSubscribers[action]){
if(component instanceof Cell){
callback = component.column.definition[action];
}else if(component instanceof Column){
callback = component.definition[action];
}
if(callback){
callback(e, componentObj);
}
}
this.dispatchExternal(action, e, componentObj);
}
}
Interaction.moduleName = "interaction";
var defaultBindings = {
navPrev:"shift + 9",
navNext:9,
navUp:38,
navDown:40,
scrollPageUp:33,
scrollPageDown:34,
scrollToStart:36,
scrollToEnd:35,
undo:["ctrl + 90", "meta + 90"],
redo:["ctrl + 89", "meta + 89"],
copyToClipboard:["ctrl + 67", "meta + 89"],
};
var defaultActions = {
keyBlock:function(e){
e.stopPropagation();
e.preventDefault();
},
scrollPageUp:function(e){
var rowManager = this.table.rowManager,
newPos = rowManager.scrollTop - rowManager.element.clientHeight;
e.preventDefault();
if(rowManager.displayRowsCount){
if(newPos >= 0){
rowManager.element.scrollTop = newPos;
}else {
rowManager.scrollToRow(rowManager.getDisplayRows()[0]);
}
}
this.table.element.focus();
},
scrollPageDown:function(e){
var rowManager = this.table.rowManager,
newPos = rowManager.scrollTop + rowManager.element.clientHeight,
scrollMax = rowManager.element.scrollHeight;
e.preventDefault();
if(rowManager.displayRowsCount){
if(newPos <= scrollMax){
rowManager.element.scrollTop = newPos;
}else {
rowManager.scrollToRow(rowManager.getDisplayRows()[rowManager.displayRowsCount - 1]);
}
}
this.table.element.focus();
},
scrollToStart:function(e){
var rowManager = this.table.rowManager;
e.preventDefault();
if(rowManager.displayRowsCount){
rowManager.scrollToRow(rowManager.getDisplayRows()[0]);
}
this.table.element.focus();
},
scrollToEnd:function(e){
var rowManager = this.table.rowManager;
e.preventDefault();
if(rowManager.displayRowsCount){
rowManager.scrollToRow(rowManager.getDisplayRows()[rowManager.displayRowsCount - 1]);
}
this.table.element.focus();
},
navPrev:function(e){
this.dispatch("keybinding-nav-prev", e);
},
navNext:function(e){
this.dispatch("keybinding-nav-next", e);
},
navLeft:function(e){
this.dispatch("keybinding-nav-left", e);
},
navRight:function(e){
this.dispatch("keybinding-nav-right", e);
},
navUp:function(e){
this.dispatch("keybinding-nav-up", e);
},
navDown:function(e){
this.dispatch("keybinding-nav-down", e);
},
undo:function(e){
var cell = false;
if(this.table.options.history && this.table.modExists("history") && this.table.modExists("edit")){
cell = this.table.modules.edit.currentCell;
if(!cell){
e.preventDefault();
this.table.modules.history.undo();
}
}
},
redo:function(e){
var cell = false;
if(this.table.options.history && this.table.modExists("history") && this.table.modExists("edit")){
cell = this.table.modules.edit.currentCell;
if(!cell){
e.preventDefault();
this.table.modules.history.redo();
}
}
},
copyToClipboard:function(e){
if(!this.table.modules.edit.currentCell){
if(this.table.modExists("clipboard", true)){
this.table.modules.clipboard.copy(false, true);
}
}
},
};
class Keybindings extends Module{
constructor(table){
super(table);
this.watchKeys = null;
this.pressedKeys = null;
this.keyupBinding = false;
this.keydownBinding = false;
this.registerTableOption("keybindings", {}); //array for keybindings
this.registerTableOption("tabEndNewRow", false); //create new row when tab to end of table
}
initialize(){
var bindings = this.table.options.keybindings,
mergedBindings = {};
this.watchKeys = {};
this.pressedKeys = [];
if(bindings !== false){
Object.assign(mergedBindings, Keybindings.bindings);
Object.assign(mergedBindings, bindings);
this.mapBindings(mergedBindings);
this.bindEvents();
}
this.subscribe("table-destroy", this.clearBindings.bind(this));
}
mapBindings(bindings){
for(let key in bindings){
if(Keybindings.actions[key]){
if(bindings[key]){
if(typeof bindings[key] !== "object"){
bindings[key] = [bindings[key]];
}
bindings[key].forEach((binding) => {
var bindingList = Array.isArray(binding) ? binding : [binding];
bindingList.forEach((item) => {
this.mapBinding(key, item);
});
});
}
}else {
console.warn("Key Binding Error - no such action:", key);
}
}
}
mapBinding(action, symbolsList){
var binding = {
action: Keybindings.actions[action],
keys: [],
ctrl: false,
shift: false,
meta: false,
};
var symbols = symbolsList.toString().toLowerCase().split(" ").join("").split("+");
symbols.forEach((symbol) => {
switch(symbol){
case "ctrl":
binding.ctrl = true;
break;
case "shift":
binding.shift = true;
break;
case "meta":
binding.meta = true;
break;
default:
symbol = isNaN(symbol) ? symbol.toUpperCase().charCodeAt(0) : parseInt(symbol);
binding.keys.push(symbol);
if(!this.watchKeys[symbol]){
this.watchKeys[symbol] = [];
}
this.watchKeys[symbol].push(binding);
}
});
}
bindEvents(){
var self = this;
this.keyupBinding = function(e){
var code = e.keyCode;
var bindings = self.watchKeys[code];
if(bindings){
self.pressedKeys.push(code);
bindings.forEach(function(binding){
self.checkBinding(e, binding);
});
}
};
this.keydownBinding = function(e){
var code = e.keyCode;
var bindings = self.watchKeys[code];
if(bindings){
var index = self.pressedKeys.indexOf(code);
if(index > -1){
self.pressedKeys.splice(index, 1);
}
}
};
this.table.element.addEventListener("keydown", this.keyupBinding);
this.table.element.addEventListener("keyup", this.keydownBinding);
}
clearBindings(){
if(this.keyupBinding){
this.table.element.removeEventListener("keydown", this.keyupBinding);
}
if(this.keydownBinding){
this.table.element.removeEventListener("keyup", this.keydownBinding);
}
}
checkBinding(e, binding){
var match = true;
if(e.ctrlKey == binding.ctrl && e.shiftKey == binding.shift && e.metaKey == binding.meta){
binding.keys.forEach((key) => {
var index = this.pressedKeys.indexOf(key);
if(index == -1){
match = false;
}
});
if(match){
binding.action.call(this, e);
}
return true;
}
return false;
}
}
Keybindings.moduleName = "keybindings";
//load defaults
Keybindings.bindings = defaultBindings;
Keybindings.actions = defaultActions;
class Menu extends Module{
constructor(table){
super(table);
this.menuContainer = null;
this.nestedMenuBlock = false;
this.currentComponent = null;
this.rootPopup = null;
this.columnSubscribers = {};
this.registerTableOption("menuContainer", undefined); //deprecated
this.registerTableOption("rowContextMenu", false);
this.registerTableOption("rowClickMenu", false);
this.registerTableOption("rowDblClickMenu", false);
this.registerTableOption("groupContextMenu", false);
this.registerTableOption("groupClickMenu", false);
this.registerTableOption("groupDblClickMenu", false);
this.registerColumnOption("headerContextMenu");
this.registerColumnOption("headerClickMenu");
this.registerColumnOption("headerDblClickMenu");
this.registerColumnOption("headerMenu");
this.registerColumnOption("headerMenuIcon");
this.registerColumnOption("contextMenu");
this.registerColumnOption("clickMenu");
this.registerColumnOption("dblClickMenu");
}
initialize(){
this.deprecatedOptionsCheck();
this.initializeRowWatchers();
this.initializeGroupWatchers();
this.subscribe("column-init", this.initializeColumn.bind(this));
}
deprecatedOptionsCheck(){
if(!this.deprecationCheck("menuContainer", "popupContainer")){
this.table.options.popupContainer = this.table.options.menuContainer;
}
}
initializeRowWatchers(){
if(this.table.options.rowContextMenu){
this.subscribe("row-contextmenu", this.loadMenuEvent.bind(this, this.table.options.rowContextMenu));
this.table.on("rowTapHold", this.loadMenuEvent.bind(this, this.table.options.rowContextMenu));
}
if(this.table.options.rowClickMenu){
this.subscribe("row-click", this.loadMenuEvent.bind(this, this.table.options.rowClickMenu));
}
if(this.table.options.rowDblClickMenu){
this.subscribe("row-dblclick", this.loadMenuEvent.bind(this, this.table.options.rowDblClickMenu));
}
}
initializeGroupWatchers(){
if(this.table.options.groupContextMenu){
this.subscribe("group-contextmenu", this.loadMenuEvent.bind(this, this.table.options.groupContextMenu));
this.table.on("groupTapHold", this.loadMenuEvent.bind(this, this.table.options.groupContextMenu));
}
if(this.table.options.groupClickMenu){
this.subscribe("group-click", this.loadMenuEvent.bind(this, this.table.options.groupClickMenu));
}
if(this.table.options.groupDblClickMenu){
this.subscribe("group-dblclick", this.loadMenuEvent.bind(this, this.table.options.groupDblClickMenu));
}
}
initializeColumn(column){
var def = column.definition;
//handle column events
if(def.headerContextMenu && !this.columnSubscribers.headerContextMenu){
this.columnSubscribers.headerContextMenu = this.loadMenuTableColumnEvent.bind(this, "headerContextMenu");
this.subscribe("column-contextmenu", this.columnSubscribers.headerContextMenu);
this.table.on("headerTapHold", this.loadMenuTableColumnEvent.bind(this, "headerContextMenu"));
}
if(def.headerClickMenu && !this.columnSubscribers.headerClickMenu){
this.columnSubscribers.headerClickMenu = this.loadMenuTableColumnEvent.bind(this, "headerClickMenu");
this.subscribe("column-click", this.columnSubscribers.headerClickMenu);
}
if(def.headerDblClickMenu && !this.columnSubscribers.headerDblClickMenu){
this.columnSubscribers.headerDblClickMenu = this.loadMenuTableColumnEvent.bind(this, "headerDblClickMenu");
this.subscribe("column-dblclick", this.columnSubscribers.headerDblClickMenu);
}
if(def.headerMenu){
this.initializeColumnHeaderMenu(column);
}
//handle cell events
if(def.contextMenu && !this.columnSubscribers.contextMenu){
this.columnSubscribers.contextMenu = this.loadMenuTableCellEvent.bind(this, "contextMenu");
this.subscribe("cell-contextmenu", this.columnSubscribers.contextMenu);
this.table.on("cellTapHold", this.loadMenuTableCellEvent.bind(this, "contextMenu"));
}
if(def.clickMenu && !this.columnSubscribers.clickMenu){
this.columnSubscribers.clickMenu = this.loadMenuTableCellEvent.bind(this, "clickMenu");
this.subscribe("cell-click", this.columnSubscribers.clickMenu);
}
if(def.dblClickMenu && !this.columnSubscribers.dblClickMenu){
this.columnSubscribers.dblClickMenu = this.loadMenuTableCellEvent.bind(this, "dblClickMenu");
this.subscribe("cell-dblclick", this.columnSubscribers.dblClickMenu);
}
}
initializeColumnHeaderMenu(column){
var icon = column.definition.headerMenuIcon,
headerMenuEl;
headerMenuEl = document.createElement("span");
headerMenuEl.classList.add("tabulator-header-popup-button");
if(icon){
if(typeof icon === "function"){
icon = icon(column.getComponent());
}
if(icon instanceof HTMLElement){
headerMenuEl.appendChild(icon);
}else {
headerMenuEl.innerHTML = icon;
}
}else {
headerMenuEl.innerHTML = "⋮";
}
headerMenuEl.addEventListener("click", (e) => {
e.stopPropagation();
e.preventDefault();
this.loadMenuEvent(column.definition.headerMenu, e, column);
});
column.titleElement.insertBefore(headerMenuEl, column.titleElement.firstChild);
}
loadMenuTableCellEvent(option, e, cell){
if(cell._cell){
cell = cell._cell;
}
if(cell.column.definition[option]){
this.loadMenuEvent(cell.column.definition[option], e, cell);
}
}
loadMenuTableColumnEvent(option, e, column){
if(column._column){
column = column._column;
}
if(column.definition[option]){
this.loadMenuEvent(column.definition[option], e, column);
}
}
loadMenuEvent(menu, e, component){
if(component._group){
component = component._group;
}else if(component._row){
component = component._row;
}
menu = typeof menu == "function" ? menu.call(this.table, e, component.getComponent()) : menu;
this.loadMenu(e, component, menu);
}
loadMenu(e, component, menu, parentEl, parentPopup){
var touch = !(e instanceof MouseEvent),
menuEl = document.createElement("div"),
popup;
menuEl.classList.add("tabulator-menu");
if(!touch){
e.preventDefault();
}
//abort if no menu set
if(!menu || !menu.length){
return;
}
if(!parentEl){
if(this.nestedMenuBlock){
//abort if child menu already open
if(this.rootPopup){
return;
}
}else {
this.nestedMenuBlock = setTimeout(() => {
this.nestedMenuBlock = false;
}, 100);
}
if(this.rootPopup){
this.rootPopup.hide();
}
this.rootPopup = popup = this.popup(menuEl);
}else {
popup = parentPopup.child(menuEl);
}
menu.forEach((item) => {
var itemEl = document.createElement("div"),
label = item.label,
disabled = item.disabled;
if(item.separator){
itemEl.classList.add("tabulator-menu-separator");
}else {
itemEl.classList.add("tabulator-menu-item");
if(typeof label == "function"){
label = label.call(this.table, component.getComponent());
}
if(label instanceof Node){
itemEl.appendChild(label);
}else {
itemEl.innerHTML = label;
}
if(typeof disabled == "function"){
disabled = disabled.call(this.table, component.getComponent());
}
if(disabled){
itemEl.classList.add("tabulator-menu-item-disabled");
itemEl.addEventListener("click", (e) => {
e.stopPropagation();
});
}else {
if(item.menu && item.menu.length){
itemEl.addEventListener("click", (e) => {
e.stopPropagation();
this.loadMenu(e, component, item.menu, itemEl, popup);
});
}else {
if(item.action){
itemEl.addEventListener("click", (e) => {
item.action(e, component.getComponent());
});
}
}
}
if(item.menu && item.menu.length){
itemEl.classList.add("tabulator-menu-item-submenu");
}
}
menuEl.appendChild(itemEl);
});
menuEl.addEventListener("click", (e) => {
if(this.rootPopup){
this.rootPopup.hide();
}
});
popup.show(parentEl || e);
if(popup === this.rootPopup){
this.rootPopup.hideOnBlur(() => {
this.rootPopup = null;
if(this.currentComponent){
this.dispatchExternal("menuClosed", this.currentComponent.getComponent());
this.currentComponent = null;
}
});
this.currentComponent = component;
this.dispatchExternal("menuOpened", component.getComponent());
}
}
}
Menu.moduleName = "menu";
class MoveColumns extends Module{
constructor(table){
super(table);
this.placeholderElement = this.createPlaceholderElement();
this.hoverElement = false; //floating column header element
this.checkTimeout = false; //click check timeout holder
this.checkPeriod = 250; //period to wait on mousedown to consider this a move and not a click
this.moving = false; //currently moving column
this.toCol = false; //destination column
this.toColAfter = false; //position of moving column relative to the destination column
this.startX = 0; //starting position within header element
this.autoScrollMargin = 40; //auto scroll on edge when within margin
this.autoScrollStep = 5; //auto scroll distance in pixels
this.autoScrollTimeout = false; //auto scroll timeout
this.touchMove = false;
this.moveHover = this.moveHover.bind(this);
this.endMove = this.endMove.bind(this);
this.registerTableOption("movableColumns", false); //enable movable columns
}
createPlaceholderElement(){
var el = document.createElement("div");
el.classList.add("tabulator-col");
el.classList.add("tabulator-col-placeholder");
return el;
}
initialize(){
if(this.table.options.movableColumns){
this.subscribe("column-init", this.initializeColumn.bind(this));
}
}
initializeColumn(column){
var self = this,
config = {},
colEl;
if(!column.modules.frozen && !column.isGroup){
colEl = column.getElement();
config.mousemove = function(e){
if(column.parent === self.moving.parent){
if((((self.touchMove ? e.touches[0].pageX : e.pageX) - Helpers.elOffset(colEl).left) + self.table.columnManager.contentsElement.scrollLeft) > (column.getWidth() / 2)){
if(self.toCol !== column || !self.toColAfter){
colEl.parentNode.insertBefore(self.placeholderElement, colEl.nextSibling);
self.moveColumn(column, true);
}
}else {
if(self.toCol !== column || self.toColAfter){
colEl.parentNode.insertBefore(self.placeholderElement, colEl);
self.moveColumn(column, false);
}
}
}
}.bind(self);
colEl.addEventListener("mousedown", function(e){
self.touchMove = false;
if(e.which === 1){
self.checkTimeout = setTimeout(function(){
self.startMove(e, column);
}, self.checkPeriod);
}
});
colEl.addEventListener("mouseup", function(e){
if(e.which === 1){
if(self.checkTimeout){
clearTimeout(self.checkTimeout);
}
}
});
self.bindTouchEvents(column);
}
column.modules.moveColumn = config;
}
bindTouchEvents(column){
var colEl = column.getElement(),
startXMove = false, //shifting center position of the cell
nextCol, prevCol, nextColWidth, prevColWidth, nextColWidthLast, prevColWidthLast;
colEl.addEventListener("touchstart", (e) => {
this.checkTimeout = setTimeout(() => {
this.touchMove = true;
nextCol = column.nextColumn();
nextColWidth = nextCol ? nextCol.getWidth()/2 : 0;
prevCol = column.prevColumn();
prevColWidth = prevCol ? prevCol.getWidth()/2 : 0;
nextColWidthLast = 0;
prevColWidthLast = 0;
startXMove = false;
this.startMove(e, column);
}, this.checkPeriod);
}, {passive: true});
colEl.addEventListener("touchmove", (e) => {
var diff, moveToCol;
if(this.moving){
this.moveHover(e);
if(!startXMove){
startXMove = e.touches[0].pageX;
}
diff = e.touches[0].pageX - startXMove;
if(diff > 0){
if(nextCol && diff - nextColWidthLast > nextColWidth){
moveToCol = nextCol;
if(moveToCol !== column){
startXMove = e.touches[0].pageX;
moveToCol.getElement().parentNode.insertBefore(this.placeholderElement, moveToCol.getElement().nextSibling);
this.moveColumn(moveToCol, true);
}
}
}else {
if(prevCol && -diff - prevColWidthLast > prevColWidth){
moveToCol = prevCol;
if(moveToCol !== column){
startXMove = e.touches[0].pageX;
moveToCol.getElement().parentNode.insertBefore(this.placeholderElement, moveToCol.getElement());
this.moveColumn(moveToCol, false);
}
}
}
if(moveToCol){
nextCol = moveToCol.nextColumn();
nextColWidthLast = nextColWidth;
nextColWidth = nextCol ? nextCol.getWidth() / 2 : 0;
prevCol = moveToCol.prevColumn();
prevColWidthLast = prevColWidth;
prevColWidth = prevCol ? prevCol.getWidth() / 2 : 0;
}
}
}, {passive: true});
colEl.addEventListener("touchend", (e) => {
if(this.checkTimeout){
clearTimeout(this.checkTimeout);
}
if(this.moving){
this.endMove(e);
}
});
}
startMove(e, column){
var element = column.getElement(),
headerElement = this.table.columnManager.getContentsElement(),
headersElement = this.table.columnManager.getHeadersElement();
this.moving = column;
this.startX = (this.touchMove ? e.touches[0].pageX : e.pageX) - Helpers.elOffset(element).left;
this.table.element.classList.add("tabulator-block-select");
//create placeholder
this.placeholderElement.style.width = column.getWidth() + "px";
this.placeholderElement.style.height = column.getHeight() + "px";
element.parentNode.insertBefore(this.placeholderElement, element);
element.parentNode.removeChild(element);
//create hover element
this.hoverElement = element.cloneNode(true);
this.hoverElement.classList.add("tabulator-moving");
headerElement.appendChild(this.hoverElement);
this.hoverElement.style.left = "0";
this.hoverElement.style.bottom = (headerElement.clientHeight - headersElement.offsetHeight) + "px";
if(!this.touchMove){
this._bindMouseMove();
document.body.addEventListener("mousemove", this.moveHover);
document.body.addEventListener("mouseup", this.endMove);
}
this.moveHover(e);
}
_bindMouseMove(){
this.table.columnManager.columnsByIndex.forEach(function(column){
if(column.modules.moveColumn.mousemove){
column.getElement().addEventListener("mousemove", column.modules.moveColumn.mousemove);
}
});
}
_unbindMouseMove(){
this.table.columnManager.columnsByIndex.forEach(function(column){
if(column.modules.moveColumn.mousemove){
column.getElement().removeEventListener("mousemove", column.modules.moveColumn.mousemove);
}
});
}
moveColumn(column, after){
var movingCells = this.moving.getCells();
this.toCol = column;
this.toColAfter = after;
if(after){
column.getCells().forEach(function(cell, i){
var cellEl = cell.getElement(true);
if(cellEl.parentNode && movingCells[i]){
cellEl.parentNode.insertBefore(movingCells[i].getElement(), cellEl.nextSibling);
}
});
}else {
column.getCells().forEach(function(cell, i){
var cellEl = cell.getElement(true);
if(cellEl.parentNode && movingCells[i]){
cellEl.parentNode.insertBefore(movingCells[i].getElement(), cellEl);
}
});
}
}
endMove(e){
if(e.which === 1 || this.touchMove){
this._unbindMouseMove();
this.placeholderElement.parentNode.insertBefore(this.moving.getElement(), this.placeholderElement.nextSibling);
this.placeholderElement.parentNode.removeChild(this.placeholderElement);
this.hoverElement.parentNode.removeChild(this.hoverElement);
this.table.element.classList.remove("tabulator-block-select");
if(this.toCol){
this.table.columnManager.moveColumnActual(this.moving, this.toCol, this.toColAfter);
}
this.moving = false;
this.toCol = false;
this.toColAfter = false;
if(!this.touchMove){
document.body.removeEventListener("mousemove", this.moveHover);
document.body.removeEventListener("mouseup", this.endMove);
}
}
}
moveHover(e){
var columnHolder = this.table.columnManager.getContentsElement(),
scrollLeft = columnHolder.scrollLeft,
xPos = ((this.touchMove ? e.touches[0].pageX : e.pageX) - Helpers.elOffset(columnHolder).left) + scrollLeft,
scrollPos;
this.hoverElement.style.left = (xPos - this.startX) + "px";
if(xPos - scrollLeft < this.autoScrollMargin){
if(!this.autoScrollTimeout){
this.autoScrollTimeout = setTimeout(() => {
scrollPos = Math.max(0,scrollLeft-5);
this.table.rowManager.getElement().scrollLeft = scrollPos;
this.autoScrollTimeout = false;
}, 1);
}
}
if(scrollLeft + columnHolder.clientWidth - xPos < this.autoScrollMargin){
if(!this.autoScrollTimeout){
this.autoScrollTimeout = setTimeout(() => {
scrollPos = Math.min(columnHolder.clientWidth, scrollLeft+5);
this.table.rowManager.getElement().scrollLeft = scrollPos;
this.autoScrollTimeout = false;
}, 1);
}
}
}
}
MoveColumns.moduleName = "moveColumn";
class MoveRows extends Module{
constructor(table){
super(table);
this.placeholderElement = this.createPlaceholderElement();
this.hoverElement = false; //floating row header element
this.checkTimeout = false; //click check timeout holder
this.checkPeriod = 150; //period to wait on mousedown to consider this a move and not a click
this.moving = false; //currently moving row
this.toRow = false; //destination row
this.toRowAfter = false; //position of moving row relative to the destination row
this.hasHandle = false; //row has handle instead of fully movable row
this.startY = 0; //starting Y position within header element
this.startX = 0; //starting X position within header element
this.moveHover = this.moveHover.bind(this);
this.endMove = this.endMove.bind(this);
this.tableRowDropEvent = false;
this.touchMove = false;
this.connection = false;
this.connectionSelectorsTables = false;
this.connectionSelectorsElements = false;
this.connectionElements = [];
this.connections = [];
this.connectedTable = false;
this.connectedRow = false;
this.registerTableOption("movableRows", false); //enable movable rows
this.registerTableOption("movableRowsConnectedTables", false); //tables for movable rows to be connected to
this.registerTableOption("movableRowsConnectedElements", false); //other elements for movable rows to be connected to
this.registerTableOption("movableRowsSender", false);
this.registerTableOption("movableRowsReceiver", "insert");
this.registerColumnOption("rowHandle");
}
createPlaceholderElement(){
var el = document.createElement("div");
el.classList.add("tabulator-row");
el.classList.add("tabulator-row-placeholder");
return el;
}
initialize(){
if(this.table.options.movableRows){
this.connectionSelectorsTables = this.table.options.movableRowsConnectedTables;
this.connectionSelectorsElements = this.table.options.movableRowsConnectedElements;
this.connection = this.connectionSelectorsTables || this.connectionSelectorsElements;
this.subscribe("cell-init", this.initializeCell.bind(this));
this.subscribe("column-init", this.initializeColumn.bind(this));
this.subscribe("row-init", this.initializeRow.bind(this));
}
}
initializeGroupHeader(group){
var self = this,
config = {};
//inter table drag drop
config.mouseup = function(e){
self.tableRowDrop(e, group);
}.bind(self);
//same table drag drop
config.mousemove = function(e){
var rowEl;
if(((e.pageY - Helpers.elOffset(group.element).top) + self.table.rowManager.element.scrollTop) > (group.getHeight() / 2)){
if(self.toRow !== group || !self.toRowAfter){
rowEl = group.getElement();
rowEl.parentNode.insertBefore(self.placeholderElement, rowEl.nextSibling);
self.moveRow(group, true);
}
}else {
if(self.toRow !== group || self.toRowAfter){
rowEl = group.getElement();
if(rowEl.previousSibling){
rowEl.parentNode.insertBefore(self.placeholderElement, rowEl);
self.moveRow(group, false);
}
}
}
}.bind(self);
group.modules.moveRow = config;
}
initializeRow(row){
var self = this,
config = {},
rowEl;
//inter table drag drop
config.mouseup = function(e){
self.tableRowDrop(e, row);
}.bind(self);
//same table drag drop
config.mousemove = function(e){
var rowEl = row.getElement();
if(((e.pageY - Helpers.elOffset(rowEl).top) + self.table.rowManager.element.scrollTop) > (row.getHeight() / 2)){
if(self.toRow !== row || !self.toRowAfter){
rowEl.parentNode.insertBefore(self.placeholderElement, rowEl.nextSibling);
self.moveRow(row, true);
}
}else {
if(self.toRow !== row || self.toRowAfter){
rowEl.parentNode.insertBefore(self.placeholderElement, rowEl);
self.moveRow(row, false);
}
}
}.bind(self);
if(!this.hasHandle){
rowEl = row.getElement();
rowEl.addEventListener("mousedown", function(e){
if(e.which === 1){
self.checkTimeout = setTimeout(function(){
self.startMove(e, row);
}, self.checkPeriod);
}
});
rowEl.addEventListener("mouseup", function(e){
if(e.which === 1){
if(self.checkTimeout){
clearTimeout(self.checkTimeout);
}
}
});
this.bindTouchEvents(row, row.getElement());
}
row.modules.moveRow = config;
}
initializeColumn(column){
if(column.definition.rowHandle && this.table.options.movableRows !== false){
this.hasHandle = true;
}
}
initializeCell(cell){
if(cell.column.definition.rowHandle && this.table.options.movableRows !== false){
var self = this,
cellEl = cell.getElement(true);
cellEl.addEventListener("mousedown", function(e){
if(e.which === 1){
self.checkTimeout = setTimeout(function(){
self.startMove(e, cell.row);
}, self.checkPeriod);
}
});
cellEl.addEventListener("mouseup", function(e){
if(e.which === 1){
if(self.checkTimeout){
clearTimeout(self.checkTimeout);
}
}
});
this.bindTouchEvents(cell.row, cellEl);
}
}
bindTouchEvents(row, element){
var startYMove = false, //shifting center position of the cell
nextRow, prevRow, nextRowHeight, prevRowHeight, nextRowHeightLast, prevRowHeightLast;
element.addEventListener("touchstart", (e) => {
this.checkTimeout = setTimeout(() => {
this.touchMove = true;
nextRow = row.nextRow();
nextRowHeight = nextRow ? nextRow.getHeight()/2 : 0;
prevRow = row.prevRow();
prevRowHeight = prevRow ? prevRow.getHeight()/2 : 0;
nextRowHeightLast = 0;
prevRowHeightLast = 0;
startYMove = false;
this.startMove(e, row);
}, this.checkPeriod);
}, {passive: true});
this.moving, this.toRow, this.toRowAfter;
element.addEventListener("touchmove", (e) => {
var diff, moveToRow;
if(this.moving){
e.preventDefault();
this.moveHover(e);
if(!startYMove){
startYMove = e.touches[0].pageY;
}
diff = e.touches[0].pageY - startYMove;
if(diff > 0){
if(nextRow && diff - nextRowHeightLast > nextRowHeight){
moveToRow = nextRow;
if(moveToRow !== row){
startYMove = e.touches[0].pageY;
moveToRow.getElement().parentNode.insertBefore(this.placeholderElement, moveToRow.getElement().nextSibling);
this.moveRow(moveToRow, true);
}
}
}else {
if(prevRow && -diff - prevRowHeightLast > prevRowHeight){
moveToRow = prevRow;
if(moveToRow !== row){
startYMove = e.touches[0].pageY;
moveToRow.getElement().parentNode.insertBefore(this.placeholderElement, moveToRow.getElement());
this.moveRow(moveToRow, false);
}
}
}
if(moveToRow){
nextRow = moveToRow.nextRow();
nextRowHeightLast = nextRowHeight;
nextRowHeight = nextRow ? nextRow.getHeight() / 2 : 0;
prevRow = moveToRow.prevRow();
prevRowHeightLast = prevRowHeight;
prevRowHeight = prevRow ? prevRow.getHeight() / 2 : 0;
}
}
});
element.addEventListener("touchend", (e) => {
if(this.checkTimeout){
clearTimeout(this.checkTimeout);
}
if(this.moving){
this.endMove(e);
this.touchMove = false;
}
});
}
_bindMouseMove(){
this.table.rowManager.getDisplayRows().forEach((row) => {
if((row.type === "row" || row.type === "group") && row.modules.moveRow && row.modules.moveRow.mousemove){
row.getElement().addEventListener("mousemove", row.modules.moveRow.mousemove);
}
});
}
_unbindMouseMove(){
this.table.rowManager.getDisplayRows().forEach((row) => {
if((row.type === "row" || row.type === "group") && row.modules.moveRow && row.modules.moveRow.mousemove){
row.getElement().removeEventListener("mousemove", row.modules.moveRow.mousemove);
}
});
}
startMove(e, row){
var element = row.getElement();
this.setStartPosition(e, row);
this.moving = row;
this.table.element.classList.add("tabulator-block-select");
//create placeholder
this.placeholderElement.style.width = row.getWidth() + "px";
this.placeholderElement.style.height = row.getHeight() + "px";
if(!this.connection){
element.parentNode.insertBefore(this.placeholderElement, element);
element.parentNode.removeChild(element);
}else {
this.table.element.classList.add("tabulator-movingrow-sending");
this.connectToTables(row);
}
//create hover element
this.hoverElement = element.cloneNode(true);
this.hoverElement.classList.add("tabulator-moving");
if(this.connection){
document.body.appendChild(this.hoverElement);
this.hoverElement.style.left = "0";
this.hoverElement.style.top = "0";
this.hoverElement.style.width = this.table.element.clientWidth + "px";
this.hoverElement.style.whiteSpace = "nowrap";
this.hoverElement.style.overflow = "hidden";
this.hoverElement.style.pointerEvents = "none";
}else {
this.table.rowManager.getTableElement().appendChild(this.hoverElement);
this.hoverElement.style.left = "0";
this.hoverElement.style.top = "0";
this._bindMouseMove();
}
document.body.addEventListener("mousemove", this.moveHover);
document.body.addEventListener("mouseup", this.endMove);
this.dispatchExternal("rowMoving", row.getComponent());
this.moveHover(e);
}
setStartPosition(e, row){
var pageX = this.touchMove ? e.touches[0].pageX : e.pageX,
pageY = this.touchMove ? e.touches[0].pageY : e.pageY,
element, position;
element = row.getElement();
if(this.connection){
position = element.getBoundingClientRect();
this.startX = position.left - pageX + window.pageXOffset;
this.startY = position.top - pageY + window.pageYOffset;
}else {
this.startY = (pageY - element.getBoundingClientRect().top);
}
}
endMove(e){
if(!e || e.which === 1 || this.touchMove){
this._unbindMouseMove();
if(!this.connection){
this.placeholderElement.parentNode.insertBefore(this.moving.getElement(), this.placeholderElement.nextSibling);
this.placeholderElement.parentNode.removeChild(this.placeholderElement);
}
this.hoverElement.parentNode.removeChild(this.hoverElement);
this.table.element.classList.remove("tabulator-block-select");
if(this.toRow){
this.table.rowManager.moveRow(this.moving, this.toRow, this.toRowAfter);
}else {
this.dispatchExternal("rowMoveCancelled", this.moving.getComponent());
}
this.moving = false;
this.toRow = false;
this.toRowAfter = false;
document.body.removeEventListener("mousemove", this.moveHover);
document.body.removeEventListener("mouseup", this.endMove);
if(this.connection){
this.table.element.classList.remove("tabulator-movingrow-sending");
this.disconnectFromTables();
}
}
}
moveRow(row, after){
this.toRow = row;
this.toRowAfter = after;
}
moveHover(e){
if(this.connection){
this.moveHoverConnections.call(this, e);
}else {
this.moveHoverTable.call(this, e);
}
}
moveHoverTable(e){
var rowHolder = this.table.rowManager.getElement(),
scrollTop = rowHolder.scrollTop,
yPos = ((this.touchMove ? e.touches[0].pageY : e.pageY) - rowHolder.getBoundingClientRect().top) + scrollTop;
this.hoverElement.style.top = Math.min(yPos - this.startY, this.table.rowManager.element.scrollHeight - this.hoverElement.offsetHeight) + "px";
}
moveHoverConnections(e){
this.hoverElement.style.left = (this.startX + (this.touchMove ? e.touches[0].pageX : e.pageX)) + "px";
this.hoverElement.style.top = (this.startY + (this.touchMove ? e.touches[0].pageY : e.pageY)) + "px";
}
elementRowDrop(e, element, row){
this.dispatchExternal("movableRowsElementDrop", e, element, row ? row.getComponent() : false);
}
//establish connection with other tables
connectToTables(row){
var connectionTables;
if(this.connectionSelectorsTables){
connectionTables = this.commsConnections(this.connectionSelectorsTables);
this.dispatchExternal("movableRowsSendingStart", connectionTables);
this.commsSend(this.connectionSelectorsTables, "moveRow", "connect", {
row:row,
});
}
if(this.connectionSelectorsElements){
this.connectionElements = [];
if(!Array.isArray(this.connectionSelectorsElements)){
this.connectionSelectorsElements = [this.connectionSelectorsElements];
}
this.connectionSelectorsElements.forEach((query) => {
if(typeof query === "string"){
this.connectionElements = this.connectionElements.concat(Array.prototype.slice.call(document.querySelectorAll(query)));
}else {
this.connectionElements.push(query);
}
});
this.connectionElements.forEach((element) => {
var dropEvent = (e) => {
this.elementRowDrop(e, element, this.moving);
};
element.addEventListener("mouseup", dropEvent);
element.tabulatorElementDropEvent = dropEvent;
element.classList.add("tabulator-movingrow-receiving");
});
}
}
//disconnect from other tables
disconnectFromTables(){
var connectionTables;
if(this.connectionSelectorsTables){
connectionTables = this.commsConnections(this.connectionSelectorsTables);
this.dispatchExternal("movableRowsSendingStop", connectionTables);
this.commsSend(this.connectionSelectorsTables, "moveRow", "disconnect");
}
this.connectionElements.forEach((element) => {
element.classList.remove("tabulator-movingrow-receiving");
element.removeEventListener("mouseup", element.tabulatorElementDropEvent);
delete element.tabulatorElementDropEvent;
});
}
//accept incomming connection
connect(table, row){
if(!this.connectedTable){
this.connectedTable = table;
this.connectedRow = row;
this.table.element.classList.add("tabulator-movingrow-receiving");
this.table.rowManager.getDisplayRows().forEach((row) => {
if(row.type === "row" && row.modules.moveRow && row.modules.moveRow.mouseup){
row.getElement().addEventListener("mouseup", row.modules.moveRow.mouseup);
}
});
this.tableRowDropEvent = this.tableRowDrop.bind(this);
this.table.element.addEventListener("mouseup", this.tableRowDropEvent);
this.dispatchExternal("movableRowsReceivingStart", row, table);
return true;
}else {
console.warn("Move Row Error - Table cannot accept connection, already connected to table:", this.connectedTable);
return false;
}
}
//close incoming connection
disconnect(table){
if(table === this.connectedTable){
this.connectedTable = false;
this.connectedRow = false;
this.table.element.classList.remove("tabulator-movingrow-receiving");
this.table.rowManager.getDisplayRows().forEach((row) =>{
if(row.type === "row" && row.modules.moveRow && row.modules.moveRow.mouseup){
row.getElement().removeEventListener("mouseup", row.modules.moveRow.mouseup);
}
});
this.table.element.removeEventListener("mouseup", this.tableRowDropEvent);
this.dispatchExternal("movableRowsReceivingStop", table);
}else {
console.warn("Move Row Error - trying to disconnect from non connected table");
}
}
dropComplete(table, row, success){
var sender = false;
if(success){
switch(typeof this.table.options.movableRowsSender){
case "string":
sender = this.senders[this.table.options.movableRowsSender];
break;
case "function":
sender = this.table.options.movableRowsSender;
break;
}
if(sender){
sender.call(this, this.moving ? this.moving.getComponent() : undefined, row ? row.getComponent() : undefined, table);
}else {
if(this.table.options.movableRowsSender){
console.warn("Mover Row Error - no matching sender found:", this.table.options.movableRowsSender);
}
}
this.dispatchExternal("movableRowsSent", this.moving.getComponent(), row ? row.getComponent() : undefined, table);
}else {
this.dispatchExternal("movableRowsSentFailed", this.moving.getComponent(), row ? row.getComponent() : undefined, table);
}
this.endMove();
}
tableRowDrop(e, row){
var receiver = false,
success = false;
e.stopImmediatePropagation();
switch(typeof this.table.options.movableRowsReceiver){
case "string":
receiver = this.receivers[this.table.options.movableRowsReceiver];
break;
case "function":
receiver = this.table.options.movableRowsReceiver;
break;
}
if(receiver){
success = receiver.call(this, this.connectedRow.getComponent(), row ? row.getComponent() : undefined, this.connectedTable);
}else {
console.warn("Mover Row Error - no matching receiver found:", this.table.options.movableRowsReceiver);
}
if(success){
this.dispatchExternal("movableRowsReceived", this.connectedRow.getComponent(), row ? row.getComponent() : undefined, this.connectedTable);
}else {
this.dispatchExternal("movableRowsReceivedFailed", this.connectedRow.getComponent(), row ? row.getComponent() : undefined, this.connectedTable);
}
this.commsSend(this.connectedTable, "moveRow", "dropcomplete", {
row:row,
success:success,
});
}
commsReceived(table, action, data){
switch(action){
case "connect":
return this.connect(table, data.row);
case "disconnect":
return this.disconnect(table);
case "dropcomplete":
return this.dropComplete(table, data.row, data.success);
}
}
}
MoveRows.prototype.receivers = {
insert:function(fromRow, toRow, fromTable){
this.table.addRow(fromRow.getData(), undefined, toRow);
return true;
},
add:function(fromRow, toRow, fromTable){
this.table.addRow(fromRow.getData());
return true;
},
update:function(fromRow, toRow, fromTable){
if(toRow){
toRow.update(fromRow.getData());
return true;
}
return false;
},
replace:function(fromRow, toRow, fromTable){
if(toRow){
this.table.addRow(fromRow.getData(), undefined, toRow);
toRow.delete();
return true;
}
return false;
},
};
MoveRows.prototype.senders = {
delete:function(fromRow, toRow, toTable){
fromRow.delete();
}
};
MoveRows.moduleName = "moveRow";
var defaultMutators = {};
class Mutator extends Module{
constructor(table){
super(table);
this.allowedTypes = ["", "data", "edit", "clipboard"]; //list of mutation types
this.enabled = true;
this.registerColumnOption("mutator");
this.registerColumnOption("mutatorParams");
this.registerColumnOption("mutatorData");
this.registerColumnOption("mutatorDataParams");
this.registerColumnOption("mutatorEdit");
this.registerColumnOption("mutatorEditParams");
this.registerColumnOption("mutatorClipboard");
this.registerColumnOption("mutatorClipboardParams");
this.registerColumnOption("mutateLink");
}
initialize(){
this.subscribe("cell-value-changing", this.transformCell.bind(this));
this.subscribe("cell-value-changed", this.mutateLink.bind(this));
this.subscribe("column-layout", this.initializeColumn.bind(this));
this.subscribe("row-data-init-before", this.rowDataChanged.bind(this));
this.subscribe("row-data-changing", this.rowDataChanged.bind(this));
}
rowDataChanged(row, tempData, updatedData){
return this.transformRow(tempData, "data", updatedData);
}
//initialize column mutator
initializeColumn(column){
var match = false,
config = {};
this.allowedTypes.forEach((type) => {
var key = "mutator" + (type.charAt(0).toUpperCase() + type.slice(1)),
mutator;
if(column.definition[key]){
mutator = this.lookupMutator(column.definition[key]);
if(mutator){
match = true;
config[key] = {
mutator:mutator,
params: column.definition[key + "Params"] || {},
};
}
}
});
if(match){
column.modules.mutate = config;
}
}
lookupMutator(value){
var mutator = false;
//set column mutator
switch(typeof value){
case "string":
if(Mutator.mutators[value]){
mutator = Mutator.mutators[value];
}else {
console.warn("Mutator Error - No such mutator found, ignoring: ", value);
}
break;
case "function":
mutator = value;
break;
}
return mutator;
}
//apply mutator to row
transformRow(data, type, updatedData){
var key = "mutator" + (type.charAt(0).toUpperCase() + type.slice(1)),
value;
if(this.enabled){
this.table.columnManager.traverse((column) => {
var mutator, params, component;
if(column.modules.mutate){
mutator = column.modules.mutate[key] || column.modules.mutate.mutator || false;
if(mutator){
value = column.getFieldValue(typeof updatedData !== "undefined" ? updatedData : data);
if((type == "data" && !updatedData)|| typeof value !== "undefined"){
component = column.getComponent();
params = typeof mutator.params === "function" ? mutator.params(value, data, type, component) : mutator.params;
column.setFieldValue(data, mutator.mutator(value, data, type, params, component));
}
}
}
});
}
return data;
}
//apply mutator to new cell value
transformCell(cell, value){
if(cell.column.modules.mutate){
var mutator = cell.column.modules.mutate.mutatorEdit || cell.column.modules.mutate.mutator || false,
tempData = {};
if(mutator){
tempData = Object.assign(tempData, cell.row.getData());
cell.column.setFieldValue(tempData, value);
return mutator.mutator(value, tempData, "edit", mutator.params, cell.getComponent());
}
}
return value;
}
mutateLink(cell){
var links = cell.column.definition.mutateLink;
if(links){
if(!Array.isArray(links)){
links = [links];
}
links.forEach((link) => {
var linkCell = cell.row.getCell(link);
if(linkCell){
linkCell.setValue(linkCell.getValue(), true, true);
}
});
}
}
enable(){
this.enabled = true;
}
disable(){
this.enabled = false;
}
}
Mutator.moduleName = "mutator";
//load defaults
Mutator.mutators = defaultMutators;
function rows(pageSize, currentRow, currentPage, totalRows, totalPages){
var el = document.createElement("span"),
showingEl = document.createElement("span"),
valueEl = document.createElement("span"),
ofEl = document.createElement("span"),
totalEl = document.createElement("span"),
rowsEl = document.createElement("span");
this.table.modules.localize.langBind("pagination|counter|showing", (value) => {
showingEl.innerHTML = value;
});
this.table.modules.localize.langBind("pagination|counter|of", (value) => {
ofEl.innerHTML = value;
});
this.table.modules.localize.langBind("pagination|counter|rows", (value) => {
rowsEl.innerHTML = value;
});
if(totalRows){
valueEl.innerHTML = " " + currentRow + "-" + Math.min((currentRow + pageSize - 1), totalRows) + " ";
totalEl.innerHTML = " " + totalRows + " ";
el.appendChild(showingEl);
el.appendChild(valueEl);
el.appendChild(ofEl);
el.appendChild(totalEl);
el.appendChild(rowsEl);
}else {
valueEl.innerHTML = " 0 ";
el.appendChild(showingEl);
el.appendChild(valueEl);
el.appendChild(rowsEl);
}
return el;
}
function pages(pageSize, currentRow, currentPage, totalRows, totalPages){
var el = document.createElement("span"),
showingEl = document.createElement("span"),
valueEl = document.createElement("span"),
ofEl = document.createElement("span"),
totalEl = document.createElement("span"),
rowsEl = document.createElement("span");
this.table.modules.localize.langBind("pagination|counter|showing", (value) => {
showingEl.innerHTML = value;
});
valueEl.innerHTML = " " + currentPage + " ";
this.table.modules.localize.langBind("pagination|counter|of", (value) => {
ofEl.innerHTML = value;
});
totalEl.innerHTML = " " + totalPages + " ";
this.table.modules.localize.langBind("pagination|counter|pages", (value) => {
rowsEl.innerHTML = value;
});
el.appendChild(showingEl);
el.appendChild(valueEl);
el.appendChild(ofEl);
el.appendChild(totalEl);
el.appendChild(rowsEl);
return el;
}
var defaultPageCounters = {
rows:rows,
pages:pages,
};
class Page extends Module{
constructor(table){
super(table);
this.mode = "local";
this.progressiveLoad = false;
this.element = null;
this.pageCounterElement = null;
this.pageCounter = null;
this.size = 0;
this.page = 1;
this.count = 5;
this.max = 1;
this.remoteRowCountEstimate = null;
this.initialLoad = true;
this.dataChanging = false; //flag to check if data is being changed by this module
this.pageSizes = [];
this.registerTableOption("pagination", false); //set pagination type
this.registerTableOption("paginationMode", "local"); //local or remote pagination
this.registerTableOption("paginationSize", false); //set number of rows to a page
this.registerTableOption("paginationInitialPage", 1); //initial page to show on load
this.registerTableOption("paginationCounter", false); // set pagination counter
this.registerTableOption("paginationCounterElement", false); // set pagination counter
this.registerTableOption("paginationButtonCount", 5); // set count of page button
this.registerTableOption("paginationSizeSelector", false); //add pagination size selector element
this.registerTableOption("paginationElement", false); //element to hold pagination numbers
// this.registerTableOption("paginationDataSent", {}); //pagination data sent to the server
// this.registerTableOption("paginationDataReceived", {}); //pagination data received from the server
this.registerTableOption("paginationAddRow", "page"); //add rows on table or page
this.registerTableOption("progressiveLoad", false); //progressive loading
this.registerTableOption("progressiveLoadDelay", 0); //delay between requests
this.registerTableOption("progressiveLoadScrollMargin", 0); //margin before scroll begins
this.registerTableFunction("setMaxPage", this.setMaxPage.bind(this));
this.registerTableFunction("setPage", this.setPage.bind(this));
this.registerTableFunction("setPageToRow", this.userSetPageToRow.bind(this));
this.registerTableFunction("setPageSize", this.userSetPageSize.bind(this));
this.registerTableFunction("getPageSize", this.getPageSize.bind(this));
this.registerTableFunction("previousPage", this.previousPage.bind(this));
this.registerTableFunction("nextPage", this.nextPage.bind(this));
this.registerTableFunction("getPage", this.getPage.bind(this));
this.registerTableFunction("getPageMax", this.getPageMax.bind(this));
//register component functions
this.registerComponentFunction("row", "pageTo", this.setPageToRow.bind(this));
}
initialize(){
if(this.table.options.pagination){
this.subscribe("row-deleted", this.rowsUpdated.bind(this));
this.subscribe("row-added", this.rowsUpdated.bind(this));
this.subscribe("data-processed", this.initialLoadComplete.bind(this));
this.subscribe("table-built", this.calculatePageSizes.bind(this));
this.subscribe("footer-redraw", this.footerRedraw.bind(this));
if(this.table.options.paginationAddRow == "page"){
this.subscribe("row-adding-position", this.rowAddingPosition.bind(this));
}
if(this.table.options.paginationMode === "remote"){
this.subscribe("data-params", this.remotePageParams.bind(this));
this.subscribe("data-loaded", this._parseRemoteData.bind(this));
}
if(this.table.options.progressiveLoad){
console.error("Progressive Load Error - Pagination and progressive load cannot be used at the same time");
}
this.registerDisplayHandler(this.restOnRenderBefore.bind(this), 40);
this.registerDisplayHandler(this.getRows.bind(this), 50);
this.createElements();
this.initializePageCounter();
this.initializePaginator();
}else if(this.table.options.progressiveLoad){
this.subscribe("data-params", this.remotePageParams.bind(this));
this.subscribe("data-loaded", this._parseRemoteData.bind(this));
this.subscribe("table-built", this.calculatePageSizes.bind(this));
this.subscribe("data-processed", this.initialLoadComplete.bind(this));
this.initializeProgressive(this.table.options.progressiveLoad);
if(this.table.options.progressiveLoad === "scroll"){
this.subscribe("scroll-vertical", this.scrollVertical.bind(this));
}
}
}
rowAddingPosition(row, top){
var rowManager = this.table.rowManager,
displayRows = rowManager.getDisplayRows(),
index;
if(top){
if(displayRows.length){
index = displayRows[0];
}else {
if(rowManager.activeRows.length){
index = rowManager.activeRows[rowManager.activeRows.length-1];
top = false;
}
}
}else {
if(displayRows.length){
index = displayRows[displayRows.length - 1];
top = displayRows.length < this.size ? false : true;
}
}
return {index, top};
}
calculatePageSizes(){
var testElRow, testElCell;
if(this.table.options.paginationSize){
this.size = this.table.options.paginationSize;
}else {
testElRow = document.createElement("div");
testElRow.classList.add("tabulator-row");
testElRow.style.visibility = "hidden";
testElCell = document.createElement("div");
testElCell.classList.add("tabulator-cell");
testElCell.innerHTML = "Page Row Test";
testElRow.appendChild(testElCell);
this.table.rowManager.getTableElement().appendChild(testElRow);
this.size = Math.floor(this.table.rowManager.getElement().clientHeight / testElRow.offsetHeight);
this.table.rowManager.getTableElement().removeChild(testElRow);
}
this.dispatchExternal("pageSizeChanged", this.size);
this.generatePageSizeSelectList();
}
initialLoadComplete(){
this.initialLoad = false;
}
remotePageParams(data, config, silent, params){
if(!this.initialLoad){
if((this.progressiveLoad && !silent) || (!this.progressiveLoad && !this.dataChanging)){
this.reset(true);
}
}
//configure request params
params.page = this.page;
//set page size if defined
if(this.size){
params.size = this.size;
}
return params;
}
///////////////////////////////////
///////// Table Functions /////////
///////////////////////////////////
userSetPageToRow(row){
if(this.table.options.pagination){
row = this.rowManager.findRow(row);
if(row){
return this.setPageToRow(row);
}
}
return Promise.reject();
}
userSetPageSize(size){
if(this.table.options.pagination){
this.setPageSize(size);
return this.setPage(1);
}else {
return false;
}
}
///////////////////////////////////
///////// Internal Logic //////////
///////////////////////////////////
scrollVertical(top, dir){
var element, diff, margin;
if(!dir && !this.table.dataLoader.loading){
element = this.table.rowManager.getElement();
diff = element.scrollHeight - element.clientHeight - top;
margin = this.table.options.progressiveLoadScrollMargin || (element.clientHeight * 2);
if(diff < margin){
this.nextPage()
.catch(() => {}); //consume the exception thrown when on the last page
}
}
}
restOnRenderBefore(rows, renderInPosition){
if(!renderInPosition){
if(this.mode === "local"){
this.reset();
}
}
return rows;
}
rowsUpdated(){
this.refreshData(true, "all");
}
createElements(){
var button;
this.element = document.createElement("span");
this.element.classList.add("tabulator-paginator");
this.pagesElement = document.createElement("span");
this.pagesElement.classList.add("tabulator-pages");
button = document.createElement("button");
button.classList.add("tabulator-page");
button.setAttribute("type", "button");
button.setAttribute("role", "button");
button.setAttribute("aria-label", "");
button.setAttribute("title", "");
this.firstBut = button.cloneNode(true);
this.firstBut.setAttribute("data-page", "first");
this.prevBut = button.cloneNode(true);
this.prevBut.setAttribute("data-page", "prev");
this.nextBut = button.cloneNode(true);
this.nextBut.setAttribute("data-page", "next");
this.lastBut = button.cloneNode(true);
this.lastBut.setAttribute("data-page", "last");
if(this.table.options.paginationSizeSelector){
this.pageSizeSelect = document.createElement("select");
this.pageSizeSelect.classList.add("tabulator-page-size");
}
}
generatePageSizeSelectList(){
var pageSizes = [];
if(this.pageSizeSelect){
if(Array.isArray(this.table.options.paginationSizeSelector)){
pageSizes = this.table.options.paginationSizeSelector;
this.pageSizes = pageSizes;
if(this.pageSizes.indexOf(this.size) == -1){
pageSizes.unshift(this.size);
}
}else {
if(this.pageSizes.indexOf(this.size) == -1){
pageSizes = [];
for (let i = 1; i < 5; i++){
pageSizes.push(this.size * i);
}
this.pageSizes = pageSizes;
}else {
pageSizes = this.pageSizes;
}
}
while(this.pageSizeSelect.firstChild) this.pageSizeSelect.removeChild(this.pageSizeSelect.firstChild);
pageSizes.forEach((item) => {
var itemEl = document.createElement("option");
itemEl.value = item;
if(item === true){
this.langBind("pagination|all", function(value){
itemEl.innerHTML = value;
});
}else {
itemEl.innerHTML = item;
}
this.pageSizeSelect.appendChild(itemEl);
});
this.pageSizeSelect.value = this.size;
}
}
initializePageCounter(){
var counter = this.table.options.paginationCounter,
pageCounter = null;
if(counter){
if(typeof counter === "function"){
pageCounter = counter;
}else {
pageCounter = Page.pageCounters[counter];
}
if(pageCounter){
this.pageCounter = pageCounter;
this.pageCounterElement = document.createElement("span");
this.pageCounterElement.classList.add("tabulator-page-counter");
}else {
console.warn("Pagination Error - No such page counter found: ", counter);
}
}
}
//setup pagination
initializePaginator(hidden){
var pageSelectLabel, paginationCounterHolder;
if(!hidden){
//build pagination element
//bind localizations
this.langBind("pagination|first", (value) => {
this.firstBut.innerHTML = value;
});
this.langBind("pagination|first_title", (value) => {
this.firstBut.setAttribute("aria-label", value);
this.firstBut.setAttribute("title", value);
});
this.langBind("pagination|prev", (value) => {
this.prevBut.innerHTML = value;
});
this.langBind("pagination|prev_title", (value) => {
this.prevBut.setAttribute("aria-label", value);
this.prevBut.setAttribute("title", value);
});
this.langBind("pagination|next", (value) => {
this.nextBut.innerHTML = value;
});
this.langBind("pagination|next_title", (value) => {
this.nextBut.setAttribute("aria-label", value);
this.nextBut.setAttribute("title", value);
});
this.langBind("pagination|last", (value) => {
this.lastBut.innerHTML = value;
});
this.langBind("pagination|last_title", (value) => {
this.lastBut.setAttribute("aria-label", value);
this.lastBut.setAttribute("title", value);
});
//click bindings
this.firstBut.addEventListener("click", () => {
this.setPage(1);
});
this.prevBut.addEventListener("click", () => {
this.previousPage();
});
this.nextBut.addEventListener("click", () => {
this.nextPage();
});
this.lastBut.addEventListener("click", () => {
this.setPage(this.max);
});
if(this.table.options.paginationElement){
this.element = this.table.options.paginationElement;
}
if(this.pageSizeSelect){
pageSelectLabel = document.createElement("label");
this.langBind("pagination|page_size", (value) => {
this.pageSizeSelect.setAttribute("aria-label", value);
this.pageSizeSelect.setAttribute("title", value);
pageSelectLabel.innerHTML = value;
});
this.element.appendChild(pageSelectLabel);
this.element.appendChild(this.pageSizeSelect);
this.pageSizeSelect.addEventListener("change", (e) => {
this.setPageSize(this.pageSizeSelect.value == "true" ? true : this.pageSizeSelect.value);
this.setPage(1);
});
}
//append to DOM
this.element.appendChild(this.firstBut);
this.element.appendChild(this.prevBut);
this.element.appendChild(this.pagesElement);
this.element.appendChild(this.nextBut);
this.element.appendChild(this.lastBut);
if(!this.table.options.paginationElement){
if(this.table.options.paginationCounter){
if(this.table.options.paginationCounterElement){
if(this.table.options.paginationCounterElement instanceof HTMLElement){
this.table.options.paginationCounterElement.appendChild(this.pageCounterElement);
}else if(typeof this.table.options.paginationCounterElement === "string"){
paginationCounterHolder = document.querySelector(this.table.options.paginationCounterElement);
if(paginationCounterHolder){
paginationCounterHolder.appendChild(this.pageCounterElement);
}else {
console.warn("Pagination Error - Unable to find element matching paginationCounterElement selector:", this.table.options.paginationCounterElement);
}
}
}else {
this.footerAppend(this.pageCounterElement);
}
}
this.footerAppend(this.element);
}
this.page = this.table.options.paginationInitialPage;
this.count = this.table.options.paginationButtonCount;
}
//set default values
this.mode = this.table.options.paginationMode;
}
initializeProgressive(mode){
this.initializePaginator(true);
this.mode = "progressive_" + mode;
this.progressiveLoad = true;
}
trackChanges(){
this.dispatch("page-changed");
}
//calculate maximum page from number of rows
setMaxRows(rowCount){
if(!rowCount){
this.max = 1;
}else {
this.max = this.size === true ? 1 : Math.ceil(rowCount/this.size);
}
if(this.page > this.max){
this.page = this.max;
}
}
//reset to first page without triggering action
reset(force){
if(!this.initialLoad){
if(this.mode == "local" || force){
this.page = 1;
this.trackChanges();
}
}
}
//set the maximum page
setMaxPage(max){
max = parseInt(max);
this.max = max || 1;
if(this.page > this.max){
this.page = this.max;
this.trigger();
}
}
//set current page number
setPage(page){
switch(page){
case "first":
return this.setPage(1);
case "prev":
return this.previousPage();
case "next":
return this.nextPage();
case "last":
return this.setPage(this.max);
}
page = parseInt(page);
if((page > 0 && page <= this.max) || this.mode !== "local"){
this.page = page;
this.trackChanges();
return this.trigger();
}else {
console.warn("Pagination Error - Requested page is out of range of 1 - " + this.max + ":", page);
return Promise.reject();
}
}
setPageToRow(row){
var rows = this.displayRows(-1);
var index = rows.indexOf(row);
if(index > -1){
var page = this.size === true ? 1 : Math.ceil((index + 1) / this.size);
return this.setPage(page);
}else {
console.warn("Pagination Error - Requested row is not visible");
return Promise.reject();
}
}
setPageSize(size){
if(size !== true){
size = parseInt(size);
}
if(size > 0){
this.size = size;
this.dispatchExternal("pageSizeChanged", size);
}
if(this.pageSizeSelect){
// this.pageSizeSelect.value = size;
this.generatePageSizeSelectList();
}
this.trackChanges();
}
_setPageCounter(totalRows, size, currentRow){
var content;
if(this.pageCounter){
if(this.mode === "remote"){
size = this.size;
currentRow = ((this.page - 1) * this.size) + 1;
totalRows = this.remoteRowCountEstimate;
}
content = this.pageCounter.call(this, size, currentRow, this.page, totalRows, this.max);
switch(typeof content){
case "object":
if(content instanceof Node){
//clear previous cell contents
while(this.pageCounterElement.firstChild) this.pageCounterElement.removeChild(this.pageCounterElement.firstChild);
this.pageCounterElement.appendChild(content);
}else {
this.pageCounterElement.innerHTML = "";
if(content != null){
console.warn("Page Counter Error - Page Counter has returned a type of object, the only valid page counter object return is an instance of Node, the page counter returned:", content);
}
}
break;
case "undefined":
this.pageCounterElement.innerHTML = "";
break;
default:
this.pageCounterElement.innerHTML = content;
}
}
}
//setup the pagination buttons
_setPageButtons(){
let leftSize = Math.floor((this.count-1) / 2);
let rightSize = Math.ceil((this.count-1) / 2);
let min = this.max - this.page + leftSize + 1 < this.count ? this.max-this.count+1: Math.max(this.page-leftSize,1);
let max = this.page <= rightSize? Math.min(this.count, this.max) :Math.min(this.page+rightSize, this.max);
while(this.pagesElement.firstChild) this.pagesElement.removeChild(this.pagesElement.firstChild);
if(this.page == 1){
this.firstBut.disabled = true;
this.prevBut.disabled = true;
}else {
this.firstBut.disabled = false;
this.prevBut.disabled = false;
}
if(this.page == this.max){
this.lastBut.disabled = true;
this.nextBut.disabled = true;
}else {
this.lastBut.disabled = false;
this.nextBut.disabled = false;
}
for(let i = min; i <= max; i++){
if(i>0 && i <= this.max){
this.pagesElement.appendChild(this._generatePageButton(i));
}
}
this.footerRedraw();
}
_generatePageButton(page){
var button = document.createElement("button");
button.classList.add("tabulator-page");
if(page == this.page){
button.classList.add("active");
}
button.setAttribute("type", "button");
button.setAttribute("role", "button");
this.langBind("pagination|page_title", (value) => {
button.setAttribute("aria-label", value + " " + page);
button.setAttribute("title", value + " " + page);
});
button.setAttribute("data-page", page);
button.textContent = page;
button.addEventListener("click", (e) => {
this.setPage(page);
});
return button;
}
//previous page
previousPage(){
if(this.page > 1){
this.page--;
this.trackChanges();
return this.trigger();
}else {
console.warn("Pagination Error - Previous page would be less than page 1:", 0);
return Promise.reject();
}
}
//next page
nextPage(){
if(this.page < this.max){
this.page++;
this.trackChanges();
return this.trigger();
}else {
if(!this.progressiveLoad){
console.warn("Pagination Error - Next page would be greater than maximum page of " + this.max + ":", this.max + 1);
}
return Promise.reject();
}
}
//return current page number
getPage(){
return this.page;
}
//return max page number
getPageMax(){
return this.max;
}
getPageSize(size){
return this.size;
}
getMode(){
return this.mode;
}
//return appropriate rows for current page
getRows(data){
var actualRowPageSize = 0,
output, start, end, actualStartRow;
var actualRows = data.filter((row) => {
return row.type === "row";
});
if(this.mode == "local"){
output = [];
this.setMaxRows(data.length);
if(this.size === true){
start = 0;
end = data.length;
}else {
start = this.size * (this.page - 1);
end = start + parseInt(this.size);
}
this._setPageButtons();
for(let i = start; i < end; i++){
let row = data[i];
if(row){
output.push(row);
if(row.type === "row"){
if(!actualStartRow){
actualStartRow = row;
}
actualRowPageSize++;
}
}
}
this._setPageCounter(actualRows.length, actualRowPageSize, actualStartRow ? (actualRows.indexOf(actualStartRow) + 1) : 0);
return output;
}else {
this._setPageButtons();
this._setPageCounter(actualRows.length);
return data.slice(0);
}
}
trigger(){
var left;
switch(this.mode){
case "local":
left = this.table.rowManager.scrollLeft;
this.refreshData();
this.table.rowManager.scrollHorizontal(left);
this.dispatchExternal("pageLoaded", this.getPage());
return Promise.resolve();
case "remote":
this.dataChanging = true;
return this.reloadData(null)
.finally(() => {
this.dataChanging = false;
});
case "progressive_load":
case "progressive_scroll":
return this.reloadData(null, true);
default:
console.warn("Pagination Error - no such pagination mode:", this.mode);
return Promise.reject();
}
}
_parseRemoteData(data){
var margin;
if(typeof data.last_page === "undefined"){
console.warn("Remote Pagination Error - Server response missing '" + (this.options("dataReceiveParams").last_page || "last_page") + "' property");
}
if(data.data){
this.max = parseInt(data.last_page) || 1;
this.remoteRowCountEstimate = typeof data.last_row !== "undefined" ? data.last_row : (data.last_page * this.size - (this.page == data.last_page ? (this.size - data.data.length) : 0));
if(this.progressiveLoad){
switch(this.mode){
case "progressive_load":
if(this.page == 1){
this.table.rowManager.setData(data.data, false, this.page == 1);
}else {
this.table.rowManager.addRows(data.data);
}
if(this.page < this.max){
setTimeout(() => {
this.nextPage();
}, this.table.options.progressiveLoadDelay);
}
break;
case "progressive_scroll":
data = this.page === 1 ? data.data : this.table.rowManager.getData().concat(data.data);
this.table.rowManager.setData(data, this.page !== 1, this.page == 1);
margin = this.table.options.progressiveLoadScrollMargin || (this.table.rowManager.element.clientHeight * 2);
if(this.table.rowManager.element.scrollHeight <= (this.table.rowManager.element.clientHeight + margin)){
if(this.page < this.max){
setTimeout(() => {
this.nextPage();
});
}
}
break;
}
return false;
}else {
// left = this.table.rowManager.scrollLeft;
this.dispatchExternal("pageLoaded", this.getPage());
// this.table.rowManager.scrollHorizontal(left);
// this.table.columnManager.scrollHorizontal(left);
}
}else {
console.warn("Remote Pagination Error - Server response missing '" + (this.options("dataReceiveParams").data || "data") + "' property");
}
return data.data;
}
//handle the footer element being redrawn
footerRedraw(){
var footer = this.table.footerManager.containerElement;
if((Math.ceil(footer.clientWidth) - footer.scrollWidth) < 0){
this.pagesElement.style.display = 'none';
}else {
this.pagesElement.style.display = '';
if((Math.ceil(footer.clientWidth) - footer.scrollWidth) < 0){
this.pagesElement.style.display = 'none';
}
}
}
}
Page.moduleName = "page";
//load defaults
Page.pageCounters = defaultPageCounters;
// read persistance information from storage
var defaultReaders = {
local:function(id, type){
var data = localStorage.getItem(id + "-" + type);
return data ? JSON.parse(data) : false;
},
cookie:function(id, type){
var cookie = document.cookie,
key = id + "-" + type,
cookiePos = cookie.indexOf(key + "="),
end, data;
//if cookie exists, decode and load column data into tabulator
if(cookiePos > -1){
cookie = cookie.slice(cookiePos);
end = cookie.indexOf(";");
if(end > -1){
cookie = cookie.slice(0, end);
}
data = cookie.replace(key + "=", "");
}
return data ? JSON.parse(data) : false;
}
};
//write persistence information to storage
var defaultWriters = {
local:function(id, type, data){
localStorage.setItem(id + "-" + type, JSON.stringify(data));
},
cookie:function(id, type, data){
var expireDate = new Date();
expireDate.setDate(expireDate.getDate() + 10000);
document.cookie = id + "-" + type + "=" + JSON.stringify(data) + "; expires=" + expireDate.toUTCString();
}
};
class Persistence extends Module{
constructor(table){
super(table);
this.mode = "";
this.id = "";
// this.persistProps = ["field", "width", "visible"];
this.defWatcherBlock = false;
this.config = {};
this.readFunc = false;
this.writeFunc = false;
this.registerTableOption("persistence", false);
this.registerTableOption("persistenceID", ""); //key for persistent storage
this.registerTableOption("persistenceMode", true); //mode for storing persistence information
this.registerTableOption("persistenceReaderFunc", false); //function for handling persistence data reading
this.registerTableOption("persistenceWriterFunc", false); //function for handling persistence data writing
}
// Test for whether localStorage is available for use.
localStorageTest() {
var testKey = "_tabulator_test";
try {
window.localStorage.setItem( testKey, testKey);
window.localStorage.removeItem( testKey );
return true;
} catch(e) {
return false;
}
}
//setup parameters
initialize(){
if(this.table.options.persistence){
//determine persistent layout storage type
var mode = this.table.options.persistenceMode,
id = this.table.options.persistenceID,
retrievedData;
this.mode = mode !== true ? mode : (this.localStorageTest() ? "local" : "cookie");
if(this.table.options.persistenceReaderFunc){
if(typeof this.table.options.persistenceReaderFunc === "function"){
this.readFunc = this.table.options.persistenceReaderFunc;
}else {
if(Persistence.readers[this.table.options.persistenceReaderFunc]){
this.readFunc = Persistence.readers[this.table.options.persistenceReaderFunc];
}else {
console.warn("Persistence Read Error - invalid reader set", this.table.options.persistenceReaderFunc);
}
}
}else {
if(Persistence.readers[this.mode]){
this.readFunc = Persistence.readers[this.mode];
}else {
console.warn("Persistence Read Error - invalid reader set", this.mode);
}
}
if(this.table.options.persistenceWriterFunc){
if(typeof this.table.options.persistenceWriterFunc === "function"){
this.writeFunc = this.table.options.persistenceWriterFunc;
}else {
if(Persistence.writers[this.table.options.persistenceWriterFunc]){
this.writeFunc = Persistence.writers[this.table.options.persistenceWriterFunc];
}else {
console.warn("Persistence Write Error - invalid reader set", this.table.options.persistenceWriterFunc);
}
}
}else {
if(Persistence.writers[this.mode]){
this.writeFunc = Persistence.writers[this.mode];
}else {
console.warn("Persistence Write Error - invalid writer set", this.mode);
}
}
//set storage tag
this.id = "tabulator-" + (id || (this.table.element.getAttribute("id") || ""));
this.config = {
sort:this.table.options.persistence === true || this.table.options.persistence.sort,
filter:this.table.options.persistence === true || this.table.options.persistence.filter,
group:this.table.options.persistence === true || this.table.options.persistence.group,
page:this.table.options.persistence === true || this.table.options.persistence.page,
columns:this.table.options.persistence === true ? ["title", "width", "visible"] : this.table.options.persistence.columns,
};
//load pagination data if needed
if(this.config.page){
retrievedData = this.retrieveData("page");
if(retrievedData){
if(typeof retrievedData.paginationSize !== "undefined" && (this.config.page === true || this.config.page.size)){
this.table.options.paginationSize = retrievedData.paginationSize;
}
if(typeof retrievedData.paginationInitialPage !== "undefined" && (this.config.page === true || this.config.page.page)){
this.table.options.paginationInitialPage = retrievedData.paginationInitialPage;
}
}
}
//load group data if needed
if(this.config.group){
retrievedData = this.retrieveData("group");
if(retrievedData){
if(typeof retrievedData.groupBy !== "undefined" && (this.config.group === true || this.config.group.groupBy)){
this.table.options.groupBy = retrievedData.groupBy;
}
if(typeof retrievedData.groupStartOpen !== "undefined" && (this.config.group === true || this.config.group.groupStartOpen)){
this.table.options.groupStartOpen = retrievedData.groupStartOpen;
}
if(typeof retrievedData.groupHeader !== "undefined" && (this.config.group === true || this.config.group.groupHeader)){
this.table.options.groupHeader = retrievedData.groupHeader;
}
}
}
if(this.config.columns){
this.table.options.columns = this.load("columns", this.table.options.columns);
this.subscribe("column-init", this.initializeColumn.bind(this));
this.subscribe("column-show", this.save.bind(this, "columns"));
this.subscribe("column-hide", this.save.bind(this, "columns"));
this.subscribe("column-moved", this.save.bind(this, "columns"));
}
this.subscribe("table-built", this.tableBuilt.bind(this), 0);
this.subscribe("table-redraw", this.tableRedraw.bind(this));
this.subscribe("filter-changed", this.eventSave.bind(this, "filter"));
this.subscribe("sort-changed", this.eventSave.bind(this, "sort"));
this.subscribe("group-changed", this.eventSave.bind(this, "group"));
this.subscribe("page-changed", this.eventSave.bind(this, "page"));
this.subscribe("column-resized", this.eventSave.bind(this, "columns"));
this.subscribe("column-width", this.eventSave.bind(this, "columns"));
this.subscribe("layout-refreshed", this.eventSave.bind(this, "columns"));
}
this.registerTableFunction("getColumnLayout", this.getColumnLayout.bind(this));
this.registerTableFunction("setColumnLayout", this.setColumnLayout.bind(this));
}
eventSave(type){
if(this.config[type]){
this.save(type);
}
}
tableBuilt(){
var sorters, filters;
if(this.config.sort){
sorters = this.load("sort");
if(!sorters === false){
this.table.options.initialSort = sorters;
}
}
if(this.config.filter){
filters = this.load("filter");
if(!filters === false){
this.table.options.initialFilter = filters;
}
}
}
tableRedraw(force){
if(force && this.config.columns){
this.save("columns");
}
}
///////////////////////////////////
///////// Table Functions /////////
///////////////////////////////////
getColumnLayout(){
return this.parseColumns(this.table.columnManager.getColumns());
}
setColumnLayout(layout){
this.table.columnManager.setColumns(this.mergeDefinition(this.table.options.columns, layout));
return true;
}
///////////////////////////////////
///////// Internal Logic //////////
///////////////////////////////////
initializeColumn(column){
var def, keys;
if(this.config.columns){
this.defWatcherBlock = true;
def = column.getDefinition();
keys = this.config.columns === true ? Object.keys(def) : this.config.columns;
keys.forEach((key)=>{
var props = Object.getOwnPropertyDescriptor(def, key);
var value = def[key];
if(props){
Object.defineProperty(def, key, {
set: (newValue) => {
value = newValue;
if(!this.defWatcherBlock){
this.save("columns");
}
if(props.set){
props.set(newValue);
}
},
get:() => {
if(props.get){
props.get();
}
return value;
}
});
}
});
this.defWatcherBlock = false;
}
}
//load saved definitions
load(type, current){
var data = this.retrieveData(type);
if(current){
data = data ? this.mergeDefinition(current, data) : current;
}
return data;
}
//retrieve data from memory
retrieveData(type){
return this.readFunc ? this.readFunc(this.id, type) : false;
}
//merge old and new column definitions
mergeDefinition(oldCols, newCols){
var output = [];
newCols = newCols || [];
newCols.forEach((column, to) => {
var from = this._findColumn(oldCols, column),
keys;
if(from){
if(this.config.columns === true || this.config.columns == undefined){
keys = Object.keys(from);
keys.push("width");
}else {
keys = this.config.columns;
}
keys.forEach((key)=>{
if(key !== "columns" && typeof column[key] !== "undefined"){
from[key] = column[key];
}
});
if(from.columns){
from.columns = this.mergeDefinition(from.columns, column.columns);
}
output.push(from);
}
});
oldCols.forEach((column, i) => {
var from = this._findColumn(newCols, column);
if (!from) {
if(output.length>i){
output.splice(i, 0, column);
}else {
output.push(column);
}
}
});
return output;
}
//find matching columns
_findColumn(columns, subject){
var type = subject.columns ? "group" : (subject.field ? "field" : "object");
return columns.find(function(col){
switch(type){
case "group":
return col.title === subject.title && col.columns.length === subject.columns.length;
case "field":
return col.field === subject.field;
case "object":
return col === subject;
}
});
}
//save data
save(type){
var data = {};
switch(type){
case "columns":
data = this.parseColumns(this.table.columnManager.getColumns());
break;
case "filter":
data = this.table.modules.filter.getFilters();
break;
case "sort":
data = this.validateSorters(this.table.modules.sort.getSort());
break;
case "group":
data = this.getGroupConfig();
break;
case "page":
data = this.getPageConfig();
break;
}
if(this.writeFunc){
this.writeFunc(this.id, type, data);
}
}
//ensure sorters contain no function data
validateSorters(data){
data.forEach(function(item){
item.column = item.field;
delete item.field;
});
return data;
}
getGroupConfig(){
var data = {};
if(this.config.group){
if(this.config.group === true || this.config.group.groupBy){
data.groupBy = this.table.options.groupBy;
}
if(this.config.group === true || this.config.group.groupStartOpen){
data.groupStartOpen = this.table.options.groupStartOpen;
}
if(this.config.group === true || this.config.group.groupHeader){
data.groupHeader = this.table.options.groupHeader;
}
}
return data;
}
getPageConfig(){
var data = {};
if(this.config.page){
if(this.config.page === true || this.config.page.size){
data.paginationSize = this.table.modules.page.getPageSize();
}
if(this.config.page === true || this.config.page.page){
data.paginationInitialPage = this.table.modules.page.getPage();
}
}
return data;
}
//parse columns for data to store
parseColumns(columns){
var definitions = [],
excludedKeys = ["headerContextMenu", "headerMenu", "contextMenu", "clickMenu"];
columns.forEach((column) => {
var defStore = {},
colDef = column.getDefinition(),
keys;
if(column.isGroup){
defStore.title = colDef.title;
defStore.columns = this.parseColumns(column.getColumns());
}else {
defStore.field = column.getField();
if(this.config.columns === true || this.config.columns == undefined){
keys = Object.keys(colDef);
keys.push("width");
keys.push("visible");
}else {
keys = this.config.columns;
}
keys.forEach((key)=>{
switch(key){
case "width":
defStore.width = column.getWidth();
break;
case "visible":
defStore.visible = column.visible;
break;
default:
if(typeof colDef[key] !== "function" && excludedKeys.indexOf(key) === -1){
defStore[key] = colDef[key];
}
}
});
}
definitions.push(defStore);
});
return definitions;
}
}
Persistence.moduleName = "persistence";
Persistence.moduleInitOrder = -10;
//load defaults
Persistence.readers = defaultReaders;
Persistence.writers = defaultWriters;
class Popup$1 extends Module{
constructor(table){
super(table);
this.columnSubscribers = {};
this.registerTableOption("rowContextPopup", false);
this.registerTableOption("rowClickPopup", false);
this.registerTableOption("rowDblClickPopup", false);
this.registerTableOption("groupContextPopup", false);
this.registerTableOption("groupClickPopup", false);
this.registerTableOption("groupDblClickPopup", false);
this.registerColumnOption("headerContextPopup");
this.registerColumnOption("headerClickPopup");
this.registerColumnOption("headerDblClickPopup");
this.registerColumnOption("headerPopup");
this.registerColumnOption("headerPopupIcon");
this.registerColumnOption("contextPopup");
this.registerColumnOption("clickPopup");
this.registerColumnOption("dblClickPopup");
this.registerComponentFunction("cell", "popup", this._componentPopupCall.bind(this));
this.registerComponentFunction("column", "popup", this._componentPopupCall.bind(this));
this.registerComponentFunction("row", "popup", this._componentPopupCall.bind(this));
this.registerComponentFunction("group", "popup", this._componentPopupCall.bind(this));
}
initialize(){
this.initializeRowWatchers();
this.initializeGroupWatchers();
this.subscribe("column-init", this.initializeColumn.bind(this));
}
_componentPopupCall(component, contents, position){
this.loadPopupEvent(contents, null, component, position);
}
initializeRowWatchers(){
if(this.table.options.rowContextPopup){
this.subscribe("row-contextmenu", this.loadPopupEvent.bind(this, this.table.options.rowContextPopup));
this.table.on("rowTapHold", this.loadPopupEvent.bind(this, this.table.options.rowContextPopup));
}
if(this.table.options.rowClickPopup){
this.subscribe("row-click", this.loadPopupEvent.bind(this, this.table.options.rowClickPopup));
}
if(this.table.options.rowDblClickPopup){
this.subscribe("row-dblclick", this.loadPopupEvent.bind(this, this.table.options.rowDblClickPopup));
}
}
initializeGroupWatchers(){
if(this.table.options.groupContextPopup){
this.subscribe("group-contextmenu", this.loadPopupEvent.bind(this, this.table.options.groupContextPopup));
this.table.on("groupTapHold", this.loadPopupEvent.bind(this, this.table.options.groupContextPopup));
}
if(this.table.options.groupClickPopup){
this.subscribe("group-click", this.loadPopupEvent.bind(this, this.table.options.groupClickPopup));
}
if(this.table.options.groupDblClickPopup){
this.subscribe("group-dblclick", this.loadPopupEvent.bind(this, this.table.options.groupDblClickPopup));
}
}
initializeColumn(column){
var def = column.definition;
//handle column events
if(def.headerContextPopup && !this.columnSubscribers.headerContextPopup){
this.columnSubscribers.headerContextPopup = this.loadPopupTableColumnEvent.bind(this, "headerContextPopup");
this.subscribe("column-contextmenu", this.columnSubscribers.headerContextPopup);
this.table.on("headerTapHold", this.loadPopupTableColumnEvent.bind(this, "headerContextPopup"));
}
if(def.headerClickPopup && !this.columnSubscribers.headerClickPopup){
this.columnSubscribers.headerClickPopup = this.loadPopupTableColumnEvent.bind(this, "headerClickPopup");
this.subscribe("column-click", this.columnSubscribers.headerClickPopup);
}if(def.headerDblClickPopup && !this.columnSubscribers.headerDblClickPopup){
this.columnSubscribers.headerDblClickPopup = this.loadPopupTableColumnEvent.bind(this, "headerDblClickPopup");
this.subscribe("column-dblclick", this.columnSubscribers.headerDblClickPopup);
}
if(def.headerPopup){
this.initializeColumnHeaderPopup(column);
}
//handle cell events
if(def.contextPopup && !this.columnSubscribers.contextPopup){
this.columnSubscribers.contextPopup = this.loadPopupTableCellEvent.bind(this, "contextPopup");
this.subscribe("cell-contextmenu", this.columnSubscribers.contextPopup);
this.table.on("cellTapHold", this.loadPopupTableCellEvent.bind(this, "contextPopup"));
}
if(def.clickPopup && !this.columnSubscribers.clickPopup){
this.columnSubscribers.clickPopup = this.loadPopupTableCellEvent.bind(this, "clickPopup");
this.subscribe("cell-click", this.columnSubscribers.clickPopup);
}
if(def.dblClickPopup && !this.columnSubscribers.dblClickPopup){
this.columnSubscribers.dblClickPopup = this.loadPopupTableCellEvent.bind(this, "dblClickPopup");
this.subscribe("cell-click", this.columnSubscribers.dblClickPopup);
}
}
initializeColumnHeaderPopup(column){
var icon = column.definition.headerPopupIcon,
headerPopupEl;
headerPopupEl = document.createElement("span");
headerPopupEl.classList.add("tabulator-header-popup-button");
if(icon){
if(typeof icon === "function"){
icon = icon(column.getComponent());
}
if(icon instanceof HTMLElement){
headerPopupEl.appendChild(icon);
}else {
headerPopupEl.innerHTML = icon;
}
}else {
headerPopupEl.innerHTML = "⋮";
}
headerPopupEl.addEventListener("click", (e) => {
e.stopPropagation();
e.preventDefault();
this.loadPopupEvent(column.definition.headerPopup, e, column);
});
column.titleElement.insertBefore(headerPopupEl, column.titleElement.firstChild);
}
loadPopupTableCellEvent(option, e, cell){
if(cell._cell){
cell = cell._cell;
}
if(cell.column.definition[option]){
this.loadPopupEvent(cell.column.definition[option], e, cell);
}
}
loadPopupTableColumnEvent(option, e, column){
if(column._column){
column = column._column;
}
if(column.definition[option]){
this.loadPopupEvent(column.definition[option], e, column);
}
}
loadPopupEvent(contents, e, component, position){
var renderedCallback;
function onRendered(callback){
renderedCallback = callback;
}
if(component._group){
component = component._group;
}else if(component._row){
component = component._row;
}
contents = typeof contents == "function" ? contents.call(this.table, e, component.getComponent(), onRendered) : contents;
this.loadPopup(e, component, contents, renderedCallback, position);
}
loadPopup(e, component, contents, renderedCallback, position){
var touch = !(e instanceof MouseEvent),
contentsEl, popup;
if(contents instanceof HTMLElement){
contentsEl = contents;
}else {
contentsEl = document.createElement("div");
contentsEl.innerHTML = contents;
}
contentsEl.classList.add("tabulator-popup");
contentsEl.addEventListener("click", (e) =>{
e.stopPropagation();
});
if(!touch){
e.preventDefault();
}
popup = this.popup(contentsEl);
if(typeof renderedCallback === "function"){
popup.renderCallback(renderedCallback);
}
if(e){
popup.show(e);
}else {
popup.show(component.getElement(), position || "center");
}
popup.hideOnBlur(() => {
this.dispatchExternal("popupClosed", component.getComponent());
});
this.dispatchExternal("popupOpened", component.getComponent());
}
}
Popup$1.moduleName = "popup";
class Print extends Module{
constructor(table){
super(table);
this.element = false;
this.manualBlock = false;
this.beforeprintEventHandler = null;
this.afterprintEventHandler = null;
this.registerTableOption("printAsHtml", false); //enable print as html
this.registerTableOption("printFormatter", false); //printing page formatter
this.registerTableOption("printHeader", false); //page header contents
this.registerTableOption("printFooter", false); //page footer contents
this.registerTableOption("printStyled", true); //enable print as html styling
this.registerTableOption("printRowRange", "visible"); //restrict print to visible rows only
this.registerTableOption("printConfig", {}); //print config options
this.registerColumnOption("print");
this.registerColumnOption("titlePrint");
}
initialize(){
if(this.table.options.printAsHtml){
this.beforeprintEventHandler = this.replaceTable.bind(this);
this.afterprintEventHandler = this.cleanup.bind(this);
window.addEventListener("beforeprint", this.beforeprintEventHandler );
window.addEventListener("afterprint", this.afterprintEventHandler);
this.subscribe("table-destroy", this.destroy.bind(this));
}
this.registerTableFunction("print", this.printFullscreen.bind(this));
}
destroy(){
if(this.table.options.printAsHtml){
window.removeEventListener( "beforeprint", this.beforeprintEventHandler );
window.removeEventListener( "afterprint", this.afterprintEventHandler );
}
}
///////////////////////////////////
///////// Table Functions /////////
///////////////////////////////////
///////////////////////////////////
///////// Internal Logic //////////
///////////////////////////////////
replaceTable(){
if(!this.manualBlock){
this.element = document.createElement("div");
this.element.classList.add("tabulator-print-table");
this.element.appendChild(this.table.modules.export.generateTable(this.table.options.printConfig, this.table.options.printStyled, this.table.options.printRowRange, "print"));
this.table.element.style.display = "none";
this.table.element.parentNode.insertBefore(this.element, this.table.element);
}
}
cleanup(){
document.body.classList.remove("tabulator-print-fullscreen-hide");
if(this.element && this.element.parentNode){
this.element.parentNode.removeChild(this.element);
this.table.element.style.display = "";
}
}
printFullscreen(visible, style, config){
var scrollX = window.scrollX,
scrollY = window.scrollY,
headerEl = document.createElement("div"),
footerEl = document.createElement("div"),
tableEl = this.table.modules.export.generateTable(typeof config != "undefined" ? config : this.table.options.printConfig, typeof style != "undefined" ? style : this.table.options.printStyled, visible || this.table.options.printRowRange, "print"),
headerContent, footerContent;
this.manualBlock = true;
this.element = document.createElement("div");
this.element.classList.add("tabulator-print-fullscreen");
if(this.table.options.printHeader){
headerEl.classList.add("tabulator-print-header");
headerContent = typeof this.table.options.printHeader == "function" ? this.table.options.printHeader.call(this.table) : this.table.options.printHeader;
if(typeof headerContent == "string"){
headerEl.innerHTML = headerContent;
}else {
headerEl.appendChild(headerContent);
}
this.element.appendChild(headerEl);
}
this.element.appendChild(tableEl);
if(this.table.options.printFooter){
footerEl.classList.add("tabulator-print-footer");
footerContent = typeof this.table.options.printFooter == "function" ? this.table.options.printFooter.call(this.table) : this.table.options.printFooter;
if(typeof footerContent == "string"){
footerEl.innerHTML = footerContent;
}else {
footerEl.appendChild(footerContent);
}
this.element.appendChild(footerEl);
}
document.body.classList.add("tabulator-print-fullscreen-hide");
document.body.appendChild(this.element);
if(this.table.options.printFormatter){
this.table.options.printFormatter(this.element, tableEl);
}
window.print();
this.cleanup();
window.scrollTo(scrollX, scrollY);
this.manualBlock = false;
}
}
Print.moduleName = "print";
class ReactiveData extends Module{
constructor(table){
super(table);
this.data = false;
this.blocked = false; //block reactivity while performing update
this.origFuncs = {}; // hold original data array functions to allow replacement after data is done with
this.currentVersion = 0;
this.registerTableOption("reactiveData", false); //enable data reactivity
}
initialize(){
if(this.table.options.reactiveData){
this.subscribe("cell-value-save-before", this.block.bind(this, "cellsave"));
this.subscribe("cell-value-save-after", this.unblock.bind(this, "cellsave"));
this.subscribe("row-data-save-before", this.block.bind(this, "rowsave"));
this.subscribe("row-data-save-after", this.unblock.bind(this, "rowsave"));
this.subscribe("row-data-init-after", this.watchRow.bind(this));
this.subscribe("data-processing", this.watchData.bind(this));
this.subscribe("table-destroy", this.unwatchData.bind(this));
}
}
watchData(data){
var self = this,
version;
this.currentVersion ++;
version = this.currentVersion;
this.unwatchData();
this.data = data;
//override array push function
this.origFuncs.push = data.push;
Object.defineProperty(this.data, "push", {
enumerable: false,
configurable: true,
value: function(){
var args = Array.from(arguments),
result;
if(!self.blocked && version === self.currentVersion){
self.block("data-push");
args.forEach((arg) => {
self.table.rowManager.addRowActual(arg, false);
});
result = self.origFuncs.push.apply(data, arguments);
self.unblock("data-push");
}
return result;
}
});
//override array unshift function
this.origFuncs.unshift = data.unshift;
Object.defineProperty(this.data, "unshift", {
enumerable: false,
configurable: true,
value: function(){
var args = Array.from(arguments),
result;
if(!self.blocked && version === self.currentVersion){
self.block("data-unshift");
args.forEach((arg) => {
self.table.rowManager.addRowActual(arg, true);
});
result = self.origFuncs.unshift.apply(data, arguments);
self.unblock("data-unshift");
}
return result;
}
});
//override array shift function
this.origFuncs.shift = data.shift;
Object.defineProperty(this.data, "shift", {
enumerable: false,
configurable: true,
value: function(){
var row, result;
if(!self.blocked && version === self.currentVersion){
self.block("data-shift");
if(self.data.length){
row = self.table.rowManager.getRowFromDataObject(self.data[0]);
if(row){
row.deleteActual();
}
}
result = self.origFuncs.shift.call(data);
self.unblock("data-shift");
}
return result;
}
});
//override array pop function
this.origFuncs.pop = data.pop;
Object.defineProperty(this.data, "pop", {
enumerable: false,
configurable: true,
value: function(){
var row, result;
if(!self.blocked && version === self.currentVersion){
self.block("data-pop");
if(self.data.length){
row = self.table.rowManager.getRowFromDataObject(self.data[self.data.length - 1]);
if(row){
row.deleteActual();
}
}
result = self.origFuncs.pop.call(data);
self.unblock("data-pop");
}
return result;
}
});
//override array splice function
this.origFuncs.splice = data.splice;
Object.defineProperty(this.data, "splice", {
enumerable: false,
configurable: true,
value: function(){
var args = Array.from(arguments),
start = args[0] < 0 ? data.length + args[0] : args[0],
end = args[1],
newRows = args[2] ? args.slice(2) : false,
startRow, result;
if(!self.blocked && version === self.currentVersion){
self.block("data-splice");
//add new rows
if(newRows){
startRow = data[start] ? self.table.rowManager.getRowFromDataObject(data[start]) : false;
if(startRow){
newRows.forEach((rowData) => {
self.table.rowManager.addRowActual(rowData, true, startRow, true);
});
}else {
newRows = newRows.slice().reverse();
newRows.forEach((rowData) => {
self.table.rowManager.addRowActual(rowData, true, false, true);
});
}
}
//delete removed rows
if(end !== 0){
var oldRows = data.slice(start, typeof args[1] === "undefined" ? args[1] : start + end);
oldRows.forEach((rowData, i) => {
var row = self.table.rowManager.getRowFromDataObject(rowData);
if(row){
row.deleteActual(i !== oldRows.length - 1);
}
});
}
if(newRows || end !== 0){
self.table.rowManager.reRenderInPosition();
}
result = self.origFuncs.splice.apply(data, arguments);
self.unblock("data-splice");
}
return result ;
}
});
}
unwatchData(){
if(this.data !== false){
for(var key in this.origFuncs){
Object.defineProperty(this.data, key, {
enumerable: true,
configurable:true,
writable:true,
value: this.origFuncs.key,
});
}
}
}
watchRow(row){
var data = row.getData();
for(var key in data){
this.watchKey(row, data, key);
}
if(this.table.options.dataTree){
this.watchTreeChildren(row);
}
}
watchTreeChildren (row){
var self = this,
childField = row.getData()[this.table.options.dataTreeChildField],
origFuncs = {};
if(childField){
origFuncs.push = childField.push;
Object.defineProperty(childField, "push", {
enumerable: false,
configurable: true,
value: () => {
if(!self.blocked){
self.block("tree-push");
var result = origFuncs.push.apply(childField, arguments);
this.rebuildTree(row);
self.unblock("tree-push");
}
return result;
}
});
origFuncs.unshift = childField.unshift;
Object.defineProperty(childField, "unshift", {
enumerable: false,
configurable: true,
value: () => {
if(!self.blocked){
self.block("tree-unshift");
var result = origFuncs.unshift.apply(childField, arguments);
this.rebuildTree(row);
self.unblock("tree-unshift");
}
return result;
}
});
origFuncs.shift = childField.shift;
Object.defineProperty(childField, "shift", {
enumerable: false,
configurable: true,
value: () => {
if(!self.blocked){
self.block("tree-shift");
var result = origFuncs.shift.call(childField);
this.rebuildTree(row);
self.unblock("tree-shift");
}
return result;
}
});
origFuncs.pop = childField.pop;
Object.defineProperty(childField, "pop", {
enumerable: false,
configurable: true,
value: () => {
if(!self.blocked){
self.block("tree-pop");
var result = origFuncs.pop.call(childField);
this.rebuildTree(row);
self.unblock("tree-pop");
}
return result;
}
});
origFuncs.splice = childField.splice;
Object.defineProperty(childField, "splice", {
enumerable: false,
configurable: true,
value: () => {
if(!self.blocked){
self.block("tree-splice");
var result = origFuncs.splice.apply(childField, arguments);
this.rebuildTree(row);
self.unblock("tree-splice");
}
return result;
}
});
}
}
rebuildTree(row){
this.table.modules.dataTree.initializeRow(row);
this.table.modules.dataTree.layoutRow(row);
this.table.rowManager.refreshActiveData("tree", false, true);
}
watchKey(row, data, key){
var self = this,
props = Object.getOwnPropertyDescriptor(data, key),
value = data[key],
version = this.currentVersion;
Object.defineProperty(data, key, {
set: (newValue) => {
value = newValue;
if(!self.blocked && version === self.currentVersion){
self.block("key");
var update = {};
update[key] = newValue;
row.updateData(update);
self.unblock("key");
}
if(props.set){
props.set(newValue);
}
},
get:() => {
if(props.get){
props.get();
}
return value;
}
});
}
unwatchRow(row){
var data = row.getData();
for(var key in data){
Object.defineProperty(data, key, {
value:data[key],
});
}
}
block(key){
if(!this.blocked){
this.blocked = key;
}
}
unblock(key){
if(this.blocked === key){
this.blocked = false;
}
}
}
ReactiveData.moduleName = "reactiveData";
class ResizeColumns extends Module{
constructor(table){
super(table);
this.startColumn = false;
this.startX = false;
this.startWidth = false;
this.latestX = false;
this.handle = null;
this.initialNextColumn = null;
this.nextColumn = null;
this.initialized = false;
this.registerColumnOption("resizable", true);
this.registerTableOption("resizableColumnFit", false);
}
initialize(){
this.subscribe("column-rendered", this.layoutColumnHeader.bind(this));
}
initializeEventWatchers(){
if(!this.initialized){
this.subscribe("cell-rendered", this.layoutCellHandles.bind(this));
this.subscribe("cell-delete", this.deInitializeComponent.bind(this));
this.subscribe("cell-height", this.resizeHandle.bind(this));
this.subscribe("column-moved", this.columnLayoutUpdated.bind(this));
this.subscribe("column-hide", this.deInitializeColumn.bind(this));
this.subscribe("column-show", this.columnLayoutUpdated.bind(this));
this.subscribe("column-width", this.columnWidthUpdated.bind(this));
this.subscribe("column-delete", this.deInitializeComponent.bind(this));
this.subscribe("column-height", this.resizeHandle.bind(this));
this.initialized = true;
}
}
layoutCellHandles(cell){
if(cell.row.type === "row"){
this.deInitializeComponent(cell);
this.initializeColumn("cell", cell, cell.column, cell.element);
}
}
layoutColumnHeader(column){
if(column.definition.resizable){
this.initializeEventWatchers();
this.deInitializeComponent(column);
this.initializeColumn("header", column, column, column.element);
}
}
columnLayoutUpdated(column){
var prev = column.prevColumn();
this.reinitializeColumn(column);
if(prev){
this.reinitializeColumn(prev);
}
}
columnWidthUpdated(column){
if(column.modules.frozen){
if(this.table.modules.frozenColumns.leftColumns.includes(column)){
this.table.modules.frozenColumns.leftColumns.forEach((col) => {
this.reinitializeColumn(col);
});
}else if(this.table.modules.frozenColumns.rightColumns.includes(column)){
this.table.modules.frozenColumns.rightColumns.forEach((col) => {
this.reinitializeColumn(col);
});
}
}
}
frozenColumnOffset(column){
var offset = false;
if(column.modules.frozen){
offset = column.modules.frozen.marginValue;
if(column.modules.frozen.position === "left"){
offset += column.getWidth() - 3;
}else {
if(offset){
offset -= 3;
}
}
}
return offset !== false ? offset + "px" : false;
}
reinitializeColumn(column){
var frozenOffset = this.frozenColumnOffset(column);
column.cells.forEach((cell) => {
if(cell.modules.resize && cell.modules.resize.handleEl){
if(frozenOffset){
cell.modules.resize.handleEl.style[column.modules.frozen.position] = frozenOffset;
}
cell.element.after(cell.modules.resize.handleEl);
}
});
if(column.modules.resize && column.modules.resize.handleEl){
if(frozenOffset){
column.modules.resize.handleEl.style[column.modules.frozen.position] = frozenOffset;
}
column.element.after(column.modules.resize.handleEl);
}
}
initializeColumn(type, component, column, element){
var self = this,
variableHeight = false,
mode = column.definition.resizable,
config = {},
nearestColumn = column.getLastColumn();
//set column resize mode
if(type === "header"){
variableHeight = column.definition.formatter == "textarea" || column.definition.variableHeight;
config = {variableHeight:variableHeight};
}
if((mode === true || mode == type) && this._checkResizability(nearestColumn)){
var handle = document.createElement('span');
handle.className = "tabulator-col-resize-handle";
handle.addEventListener("click", function(e){
e.stopPropagation();
});
var handleDown = function(e){
self.startColumn = column;
self.initialNextColumn = self.nextColumn = nearestColumn.nextColumn();
self._mouseDown(e, nearestColumn, handle);
};
handle.addEventListener("mousedown", handleDown);
handle.addEventListener("touchstart", handleDown, {passive: true});
//resize column on double click
handle.addEventListener("dblclick", (e) => {
var oldWidth = nearestColumn.getWidth();
e.stopPropagation();
nearestColumn.reinitializeWidth(true);
if(oldWidth !== nearestColumn.getWidth()){
self.dispatch("column-resized", nearestColumn);
self.table.externalEvents.dispatch("columnResized", nearestColumn.getComponent());
}
});
if(column.modules.frozen){
handle.style.position = "sticky";
handle.style[column.modules.frozen.position] = this.frozenColumnOffset(column);
}
config.handleEl = handle;
if(element.parentNode && column.visible){
element.after(handle);
}
}
component.modules.resize = config;
}
deInitializeColumn(column){
this.deInitializeComponent(column);
column.cells.forEach((cell) => {
this.deInitializeComponent(cell);
});
}
deInitializeComponent(component){
var handleEl;
if(component.modules.resize){
handleEl = component.modules.resize.handleEl;
if(handleEl && handleEl.parentElement){
handleEl.parentElement.removeChild(handleEl);
}
}
}
resizeHandle(component, height){
if(component.modules.resize && component.modules.resize.handleEl){
component.modules.resize.handleEl.style.height = height;
}
}
_checkResizability(column){
return column.definition.resizable;
}
_mouseDown(e, column, handle){
var self = this;
self.table.element.classList.add("tabulator-block-select");
function mouseMove(e){
var x = typeof e.screenX === "undefined" ? e.touches[0].screenX : e.screenX,
startDiff = x - self.startX,
moveDiff = x - self.latestX,
blockedBefore, blockedAfter;
self.latestX = x;
if(self.table.rtl){
startDiff = -startDiff;
moveDiff = -moveDiff;
}
blockedBefore = column.width == column.minWidth || column.width == column.maxWidth;
column.setWidth(self.startWidth + startDiff);
blockedAfter = column.width == column.minWidth || column.width == column.maxWidth;
if(moveDiff < 0){
self.nextColumn = self.initialNextColumn;
}
if(self.table.options.resizableColumnFit && self.nextColumn && !(blockedBefore && blockedAfter)){
let colWidth = self.nextColumn.getWidth();
if(moveDiff > 0){
if(colWidth <= self.nextColumn.minWidth){
self.nextColumn = self.nextColumn.nextColumn();
}
}
if(self.nextColumn){
self.nextColumn.setWidth(self.nextColumn.getWidth() - moveDiff);
}
}
self.table.columnManager.rerenderColumns(true);
if(!self.table.browserSlow && column.modules.resize && column.modules.resize.variableHeight){
column.checkCellHeights();
}
}
function mouseUp(e){
//block editor from taking action while resizing is taking place
if(self.startColumn.modules.edit){
self.startColumn.modules.edit.blocked = false;
}
if(self.table.browserSlow && column.modules.resize && column.modules.resize.variableHeight){
column.checkCellHeights();
}
document.body.removeEventListener("mouseup", mouseUp);
document.body.removeEventListener("mousemove", mouseMove);
handle.removeEventListener("touchmove", mouseMove);
handle.removeEventListener("touchend", mouseUp);
self.table.element.classList.remove("tabulator-block-select");
if(self.startWidth !== column.getWidth()){
self.table.columnManager.verticalAlignHeaders();
self.dispatch("column-resized", column);
self.table.externalEvents.dispatch("columnResized", column.getComponent());
}
}
e.stopPropagation(); //prevent resize from interfering with movable columns
//block editor from taking action while resizing is taking place
if(self.startColumn.modules.edit){
self.startColumn.modules.edit.blocked = true;
}
self.startX = typeof e.screenX === "undefined" ? e.touches[0].screenX : e.screenX;
self.latestX = self.startX;
self.startWidth = column.getWidth();
document.body.addEventListener("mousemove", mouseMove);
document.body.addEventListener("mouseup", mouseUp);
handle.addEventListener("touchmove", mouseMove, {passive: true});
handle.addEventListener("touchend", mouseUp);
}
}
ResizeColumns.moduleName = "resizeColumns";
class ResizeRows extends Module{
constructor(table){
super(table);
this.startColumn = false;
this.startY = false;
this.startHeight = false;
this.handle = null;
this.prevHandle = null;
this.registerTableOption("resizableRows", false); //resizable rows
}
initialize(){
if(this.table.options.resizableRows){
this.subscribe("row-layout-after", this.initializeRow.bind(this));
}
}
initializeRow(row){
var self = this,
rowEl = row.getElement();
var handle = document.createElement('div');
handle.className = "tabulator-row-resize-handle";
var prevHandle = document.createElement('div');
prevHandle.className = "tabulator-row-resize-handle prev";
handle.addEventListener("click", function(e){
e.stopPropagation();
});
var handleDown = function(e){
self.startRow = row;
self._mouseDown(e, row, handle);
};
handle.addEventListener("mousedown", handleDown);
handle.addEventListener("touchstart", handleDown, {passive: true});
prevHandle.addEventListener("click", function(e){
e.stopPropagation();
});
var prevHandleDown = function(e){
var prevRow = self.table.rowManager.prevDisplayRow(row);
if(prevRow){
self.startRow = prevRow;
self._mouseDown(e, prevRow, prevHandle);
}
};
prevHandle.addEventListener("mousedown",prevHandleDown);
prevHandle.addEventListener("touchstart",prevHandleDown, {passive: true});
rowEl.appendChild(handle);
rowEl.appendChild(prevHandle);
}
_mouseDown(e, row, handle){
var self = this;
self.table.element.classList.add("tabulator-block-select");
function mouseMove(e){
row.setHeight(self.startHeight + ((typeof e.screenY === "undefined" ? e.touches[0].screenY : e.screenY) - self.startY));
}
function mouseUp(e){
// //block editor from taking action while resizing is taking place
// if(self.startColumn.modules.edit){
// self.startColumn.modules.edit.blocked = false;
// }
document.body.removeEventListener("mouseup", mouseMove);
document.body.removeEventListener("mousemove", mouseMove);
handle.removeEventListener("touchmove", mouseMove);
handle.removeEventListener("touchend", mouseUp);
self.table.element.classList.remove("tabulator-block-select");
self.dispatchExternal("rowResized", row.getComponent());
}
e.stopPropagation(); //prevent resize from interfering with movable columns
//block editor from taking action while resizing is taking place
// if(self.startColumn.modules.edit){
// self.startColumn.modules.edit.blocked = true;
// }
self.startY = typeof e.screenY === "undefined" ? e.touches[0].screenY : e.screenY;
self.startHeight = row.getHeight();
document.body.addEventListener("mousemove", mouseMove);
document.body.addEventListener("mouseup", mouseUp);
handle.addEventListener("touchmove", mouseMove, {passive: true});
handle.addEventListener("touchend", mouseUp);
}
}
ResizeRows.moduleName = "resizeRows";
class ResizeTable extends Module{
constructor(table){
super(table);
this.binding = false;
this.visibilityObserver = false;
this.resizeObserver = false;
this.containerObserver = false;
this.tableHeight = 0;
this.tableWidth = 0;
this.containerHeight = 0;
this.containerWidth = 0;
this.autoResize = false;
this.visible = false;
this.initialized = false;
this.initialRedraw = false;
this.registerTableOption("autoResize", true); //auto resize table
}
initialize(){
if(this.table.options.autoResize){
var table = this.table,
tableStyle;
this.tableHeight = table.element.clientHeight;
this.tableWidth = table.element.clientWidth;
if(table.element.parentNode){
this.containerHeight = table.element.parentNode.clientHeight;
this.containerWidth = table.element.parentNode.clientWidth;
}
if(typeof IntersectionObserver !== "undefined" && typeof ResizeObserver !== "undefined" && table.rowManager.getRenderMode() === "virtual"){
this.initializeVisibilityObserver();
this.autoResize = true;
this.resizeObserver = new ResizeObserver((entry) => {
if(!table.browserMobile || (table.browserMobile &&!table.modules.edit.currentCell)){
var nodeHeight = Math.floor(entry[0].contentRect.height);
var nodeWidth = Math.floor(entry[0].contentRect.width);
if(this.tableHeight != nodeHeight || this.tableWidth != nodeWidth){
this.tableHeight = nodeHeight;
this.tableWidth = nodeWidth;
if(table.element.parentNode){
this.containerHeight = table.element.parentNode.clientHeight;
this.containerWidth = table.element.parentNode.clientWidth;
}
this.redrawTable();
}
}
});
this.resizeObserver.observe(table.element);
tableStyle = window.getComputedStyle(table.element);
if(this.table.element.parentNode && !this.table.rowManager.fixedHeight && (tableStyle.getPropertyValue("max-height") || tableStyle.getPropertyValue("min-height"))){
this.containerObserver = new ResizeObserver((entry) => {
if(!table.browserMobile || (table.browserMobile &&!table.modules.edit.currentCell)){
var nodeHeight = Math.floor(entry[0].contentRect.height);
var nodeWidth = Math.floor(entry[0].contentRect.width);
if(this.containerHeight != nodeHeight || this.containerWidth != nodeWidth){
this.containerHeight = nodeHeight;
this.containerWidth = nodeWidth;
this.tableHeight = table.element.clientHeight;
this.tableWidth = table.element.clientWidth;
}
this.redrawTable();
}
});
this.containerObserver.observe(this.table.element.parentNode);
}
this.subscribe("table-resize", this.tableResized.bind(this));
}else {
this.binding = function(){
if(!table.browserMobile || (table.browserMobile && !table.modules.edit.currentCell)){
table.columnManager.rerenderColumns(true);
table.redraw();
}
};
window.addEventListener("resize", this.binding);
}
this.subscribe("table-destroy", this.clearBindings.bind(this));
}
}
initializeVisibilityObserver(){
this.visibilityObserver = new IntersectionObserver((entries) => {
this.visible = entries[0].isIntersecting;
if(!this.initialized){
this.initialized = true;
this.initialRedraw = !this.visible;
}else {
if(this.visible){
this.redrawTable(this.initialRedraw);
this.initialRedraw = false;
}
}
});
this.visibilityObserver.observe(this.table.element);
}
redrawTable(force){
if(this.initialized && this.visible){
this.table.columnManager.rerenderColumns(true);
this.table.redraw(force);
}
}
tableResized(){
this.table.rowManager.redraw();
}
clearBindings(){
if(this.binding){
window.removeEventListener("resize", this.binding);
}
if(this.resizeObserver){
this.resizeObserver.unobserve(this.table.element);
}
if(this.visibilityObserver){
this.visibilityObserver.unobserve(this.table.element);
}
if(this.containerObserver){
this.containerObserver.unobserve(this.table.element.parentNode);
}
}
}
ResizeTable.moduleName = "resizeTable";
class ResponsiveLayout extends Module{
constructor(table){
super(table);
this.columns = [];
this.hiddenColumns = [];
this.mode = "";
this.index = 0;
this.collapseFormatter = [];
this.collapseStartOpen = true;
this.collapseHandleColumn = false;
this.registerTableOption("responsiveLayout", false); //responsive layout flags
this.registerTableOption("responsiveLayoutCollapseStartOpen", true); //start showing collapsed data
this.registerTableOption("responsiveLayoutCollapseUseFormatters", true); //responsive layout collapse formatter
this.registerTableOption("responsiveLayoutCollapseFormatter", false); //responsive layout collapse formatter
this.registerColumnOption("responsive");
}
//generate responsive columns list
initialize(){
if(this.table.options.responsiveLayout){
this.subscribe("column-layout", this.initializeColumn.bind(this));
this.subscribe("column-show", this.updateColumnVisibility.bind(this));
this.subscribe("column-hide", this.updateColumnVisibility.bind(this));
this.subscribe("columns-loaded", this.initializeResponsivity.bind(this));
this.subscribe("column-moved", this.initializeResponsivity.bind(this));
this.subscribe("column-add", this.initializeResponsivity.bind(this));
this.subscribe("column-delete", this.initializeResponsivity.bind(this));
this.subscribe("table-redrawing", this.tableRedraw.bind(this));
if(this.table.options.responsiveLayout === "collapse"){
this.subscribe("row-data-changed", this.generateCollapsedRowContent.bind(this));
this.subscribe("row-init", this.initializeRow.bind(this));
this.subscribe("row-layout", this.layoutRow.bind(this));
}
}
}
tableRedraw(force){
if(["fitColumns", "fitDataStretch"].indexOf(this.layoutMode()) === -1){
if(!force){
this.update();
}
}
}
initializeResponsivity(){
var columns = [];
this.mode = this.table.options.responsiveLayout;
this.collapseFormatter = this.table.options.responsiveLayoutCollapseFormatter || this.formatCollapsedData;
this.collapseStartOpen = this.table.options.responsiveLayoutCollapseStartOpen;
this.hiddenColumns = [];
//determine level of responsivity for each column
this.table.columnManager.columnsByIndex.forEach((column, i) => {
if(column.modules.responsive){
if(column.modules.responsive.order && column.modules.responsive.visible){
column.modules.responsive.index = i;
columns.push(column);
if(!column.visible && this.mode === "collapse"){
this.hiddenColumns.push(column);
}
}
}
});
//sort list by responsivity
columns = columns.reverse();
columns = columns.sort((a, b) => {
var diff = b.modules.responsive.order - a.modules.responsive.order;
return diff || (b.modules.responsive.index - a.modules.responsive.index);
});
this.columns = columns;
if(this.mode === "collapse"){
this.generateCollapsedContent();
}
//assign collapse column
for (let col of this.table.columnManager.columnsByIndex){
if(col.definition.formatter == "responsiveCollapse"){
this.collapseHandleColumn = col;
break;
}
}
if(this.collapseHandleColumn){
if(this.hiddenColumns.length){
this.collapseHandleColumn.show();
}else {
this.collapseHandleColumn.hide();
}
}
}
//define layout information
initializeColumn(column){
var def = column.getDefinition();
column.modules.responsive = {order: typeof def.responsive === "undefined" ? 1 : def.responsive, visible:def.visible === false ? false : true};
}
initializeRow(row){
var el;
if(row.type !== "calc"){
el = document.createElement("div");
el.classList.add("tabulator-responsive-collapse");
row.modules.responsiveLayout = {
element:el,
open:this.collapseStartOpen,
};
if(!this.collapseStartOpen){
el.style.display = 'none';
}
}
}
layoutRow(row){
var rowEl = row.getElement();
if(row.modules.responsiveLayout){
rowEl.appendChild(row.modules.responsiveLayout.element);
this.generateCollapsedRowContent(row);
}
}
//update column visibility
updateColumnVisibility(column, responsiveToggle){
if(!responsiveToggle && column.modules.responsive){
column.modules.responsive.visible = column.visible;
this.initializeResponsivity();
}
}
hideColumn(column){
var colCount = this.hiddenColumns.length;
column.hide(false, true);
if(this.mode === "collapse"){
this.hiddenColumns.unshift(column);
this.generateCollapsedContent();
if(this.collapseHandleColumn && !colCount){
this.collapseHandleColumn.show();
}
}
}
showColumn(column){
var index;
column.show(false, true);
//set column width to prevent calculation loops on uninitialized columns
column.setWidth(column.getWidth());
if(this.mode === "collapse"){
index = this.hiddenColumns.indexOf(column);
if(index > -1){
this.hiddenColumns.splice(index, 1);
}
this.generateCollapsedContent();
if(this.collapseHandleColumn && !this.hiddenColumns.length){
this.collapseHandleColumn.hide();
}
}
}
//redraw columns to fit space
update(){
var working = true;
while(working){
let width = this.table.modules.layout.getMode() == "fitColumns" ? this.table.columnManager.getFlexBaseWidth() : this.table.columnManager.getWidth();
let diff = (this.table.options.headerVisible ? this.table.columnManager.element.clientWidth : this.table.element.clientWidth) - width;
if(diff < 0){
//table is too wide
let column = this.columns[this.index];
if(column){
this.hideColumn(column);
this.index ++;
}else {
working = false;
}
}else {
//table has spare space
let column = this.columns[this.index -1];
if(column){
if(diff > 0){
if(diff >= column.getWidth()){
this.showColumn(column);
this.index --;
}else {
working = false;
}
}else {
working = false;
}
}else {
working = false;
}
}
if(!this.table.rowManager.activeRowsCount){
this.table.rowManager.renderEmptyScroll();
}
}
}
generateCollapsedContent(){
var rows = this.table.rowManager.getDisplayRows();
rows.forEach((row) => {
this.generateCollapsedRowContent(row);
});
}
generateCollapsedRowContent(row){
var el, contents;
if(row.modules.responsiveLayout){
el = row.modules.responsiveLayout.element;
while(el.firstChild) el.removeChild(el.firstChild);
contents = this.collapseFormatter(this.generateCollapsedRowData(row));
if(contents){
el.appendChild(contents);
}
}
}
generateCollapsedRowData(row){
var data = row.getData(),
output = [],
mockCellComponent;
this.hiddenColumns.forEach((column) => {
var value = column.getFieldValue(data);
if(column.definition.title && column.field){
if(column.modules.format && this.table.options.responsiveLayoutCollapseUseFormatters){
mockCellComponent = {
value:false,
data:{},
getValue:function(){
return value;
},
getData:function(){
return data;
},
getElement:function(){
return document.createElement("div");
},
getRow:function(){
return row.getComponent();
},
getColumn:function(){
return column.getComponent();
},
getTable:() => {
return this.table;
},
};
function onRendered(callback){
callback();
}
output.push({
field: column.field,
title: column.definition.title,
value: column.modules.format.formatter.call(this.table.modules.format, mockCellComponent, column.modules.format.params, onRendered)
});
}else {
output.push({
field: column.field,
title: column.definition.title,
value: value
});
}
}
});
return output;
}
formatCollapsedData(data){
var list = document.createElement("table");
data.forEach(function(item){
var row = document.createElement("tr");
var titleData = document.createElement("td");
var valueData = document.createElement("td");
var node_content;
var titleHighlight = document.createElement("strong");
titleData.appendChild(titleHighlight);
this.langBind("columns|" + item.field, function(text){
titleHighlight.innerHTML = text || item.title;
});
if(item.value instanceof Node){
node_content = document.createElement("div");
node_content.appendChild(item.value);
valueData.appendChild(node_content);
}else {
valueData.innerHTML = item.value;
}
row.appendChild(titleData);
row.appendChild(valueData);
list.appendChild(row);
}, this);
return Object.keys(data).length ? list : "";
}
}
ResponsiveLayout.moduleName = "responsiveLayout";
class SelectRow extends Module{
constructor(table){
super(table);
this.selecting = false; //flag selecting in progress
this.lastClickedRow = false; //last clicked row
this.selectPrev = []; //hold previously selected element for drag drop selection
this.selectedRows = []; //hold selected rows
this.headerCheckboxElement = null; // hold header select element
this.registerTableOption("selectable", "highlight"); //highlight rows on hover
this.registerTableOption("selectableRangeMode", "drag"); //highlight rows on hover
this.registerTableOption("selectableRollingSelection", true); //roll selection once maximum number of selectable rows is reached
this.registerTableOption("selectablePersistence", true); // maintain selection when table view is updated
this.registerTableOption("selectableCheck", function(data, row){return true;}); //check whether row is selectable
this.registerTableFunction("selectRow", this.selectRows.bind(this));
this.registerTableFunction("deselectRow", this.deselectRows.bind(this));
this.registerTableFunction("toggleSelectRow", this.toggleRow.bind(this));
this.registerTableFunction("getSelectedRows", this.getSelectedRows.bind(this));
this.registerTableFunction("getSelectedData", this.getSelectedData.bind(this));
//register component functions
this.registerComponentFunction("row", "select", this.selectRows.bind(this));
this.registerComponentFunction("row", "deselect", this.deselectRows.bind(this));
this.registerComponentFunction("row", "toggleSelect", this.toggleRow.bind(this));
this.registerComponentFunction("row", "isSelected", this.isRowSelected.bind(this));
}
initialize(){
if(this.table.options.selectable !== false){
this.subscribe("row-init", this.initializeRow.bind(this));
this.subscribe("row-deleting", this.rowDeleted.bind(this));
this.subscribe("rows-wipe", this.clearSelectionData.bind(this));
this.subscribe("rows-retrieve", this.rowRetrieve.bind(this));
if(this.table.options.selectable && !this.table.options.selectablePersistence){
this.subscribe("data-refreshing", this.deselectRows.bind(this));
}
}
}
rowRetrieve(type, prevValue){
return type === "selected" ? this.selectedRows : prevValue;
}
rowDeleted(row){
this._deselectRow(row, true);
}
clearSelectionData(silent){
var prevSelected = this.selectedRows.length;
this.selecting = false;
this.lastClickedRow = false;
this.selectPrev = [];
this.selectedRows = [];
if(prevSelected && silent !== true){
this._rowSelectionChanged();
}
}
initializeRow(row){
var self = this,
element = row.getElement();
// trigger end of row selection
var endSelect = function(){
setTimeout(function(){
self.selecting = false;
}, 50);
document.body.removeEventListener("mouseup", endSelect);
};
row.modules.select = {selected:false};
//set row selection class
if(self.checkRowSelectability(row)){
element.classList.add("tabulator-selectable");
element.classList.remove("tabulator-unselectable");
if(self.table.options.selectable && self.table.options.selectable != "highlight"){
if(self.table.options.selectableRangeMode === "click"){
element.addEventListener("click", this.handleComplexRowClick.bind(this, row));
}else {
element.addEventListener("click", function(e){
if(!self.table.modExists("edit") || !self.table.modules.edit.getCurrentCell()){
self.table._clearSelection();
}
if(!self.selecting){
self.toggleRow(row);
}
});
element.addEventListener("mousedown", function(e){
if(e.shiftKey){
self.table._clearSelection();
self.selecting = true;
self.selectPrev = [];
document.body.addEventListener("mouseup", endSelect);
document.body.addEventListener("keyup", endSelect);
self.toggleRow(row);
return false;
}
});
element.addEventListener("mouseenter", function(e){
if(self.selecting){
self.table._clearSelection();
self.toggleRow(row);
if(self.selectPrev[1] == row){
self.toggleRow(self.selectPrev[0]);
}
}
});
element.addEventListener("mouseout", function(e){
if(self.selecting){
self.table._clearSelection();
self.selectPrev.unshift(row);
}
});
}
}
}else {
element.classList.add("tabulator-unselectable");
element.classList.remove("tabulator-selectable");
}
}
handleComplexRowClick(row, e){
if(e.shiftKey){
this.table._clearSelection();
this.lastClickedRow = this.lastClickedRow || row;
var lastClickedRowIdx = this.table.rowManager.getDisplayRowIndex(this.lastClickedRow);
var rowIdx = this.table.rowManager.getDisplayRowIndex(row);
var fromRowIdx = lastClickedRowIdx <= rowIdx ? lastClickedRowIdx : rowIdx;
var toRowIdx = lastClickedRowIdx >= rowIdx ? lastClickedRowIdx : rowIdx;
var rows = this.table.rowManager.getDisplayRows().slice(0);
var toggledRows = rows.splice(fromRowIdx, toRowIdx - fromRowIdx + 1);
if(e.ctrlKey || e.metaKey){
toggledRows.forEach((toggledRow)=>{
if(toggledRow !== this.lastClickedRow){
if(this.table.options.selectable !== true && !this.isRowSelected(row)){
if(this.selectedRows.length < this.table.options.selectable){
this.toggleRow(toggledRow);
}
}else {
this.toggleRow(toggledRow);
}
}
});
this.lastClickedRow = row;
}else {
this.deselectRows(undefined, true);
if(this.table.options.selectable !== true){
if(toggledRows.length > this.table.options.selectable){
toggledRows = toggledRows.slice(0, this.table.options.selectable);
}
}
this.selectRows(toggledRows);
}
this.table._clearSelection();
}
else if(e.ctrlKey || e.metaKey){
this.toggleRow(row);
this.lastClickedRow = row;
}else {
this.deselectRows(undefined, true);
this.selectRows(row);
this.lastClickedRow = row;
}
}
checkRowSelectability(row){
if(row.type === "row"){
return this.table.options.selectableCheck.call(this.table, row.getComponent());
}
return false;
}
//toggle row selection
toggleRow(row){
if(this.checkRowSelectability(row)){
if(row.modules.select && row.modules.select.selected){
this._deselectRow(row);
}else {
this._selectRow(row);
}
}
}
//select a number of rows
selectRows(rows){
var rowMatch;
switch(typeof rows){
case "undefined":
this.table.rowManager.rows.forEach((row) => {
this._selectRow(row, true, true);
});
this._rowSelectionChanged();
break;
case "string":
rowMatch = this.table.rowManager.findRow(rows);
if(rowMatch){
this._selectRow(rowMatch, true, true);
this._rowSelectionChanged();
}else {
rowMatch = this.table.rowManager.getRows(rows);
rowMatch.forEach((row) => {
this._selectRow(row, true, true);
});
if(rowMatch.length){
this._rowSelectionChanged();
}
}
break;
default:
if(Array.isArray(rows)){
rows.forEach((row) => {
this._selectRow(row, true, true);
});
this._rowSelectionChanged();
}else {
this._selectRow(rows, false, true);
}
break;
}
}
//select an individual row
_selectRow(rowInfo, silent, force){
//handle max row count
if(!isNaN(this.table.options.selectable) && this.table.options.selectable !== true && !force){
if(this.selectedRows.length >= this.table.options.selectable){
if(this.table.options.selectableRollingSelection){
this._deselectRow(this.selectedRows[0]);
}else {
return false;
}
}
}
var row = this.table.rowManager.findRow(rowInfo);
if(row){
if(this.selectedRows.indexOf(row) == -1){
row.getElement().classList.add("tabulator-selected");
if(!row.modules.select){
row.modules.select = {};
}
row.modules.select.selected = true;
if(row.modules.select.checkboxEl){
row.modules.select.checkboxEl.checked = true;
}
this.selectedRows.push(row);
if(this.table.options.dataTreeSelectPropagate){
this.childRowSelection(row, true);
}
this.dispatchExternal("rowSelected", row.getComponent());
this._rowSelectionChanged(silent);
}
}else {
if(!silent){
console.warn("Selection Error - No such row found, ignoring selection:" + rowInfo);
}
}
}
isRowSelected(row){
return this.selectedRows.indexOf(row) !== -1;
}
//deselect a number of rows
deselectRows(rows, silent){
var self = this,
rowCount;
if(typeof rows == "undefined"){
rowCount = self.selectedRows.length;
for(let i = 0; i < rowCount; i++){
self._deselectRow(self.selectedRows[0], true);
}
if(rowCount){
self._rowSelectionChanged(silent);
}
}else {
if(Array.isArray(rows)){
rows.forEach(function(row){
self._deselectRow(row, true);
});
self._rowSelectionChanged(silent);
}else {
self._deselectRow(rows, silent);
}
}
}
//deselect an individual row
_deselectRow(rowInfo, silent){
var self = this,
row = self.table.rowManager.findRow(rowInfo),
index;
if(row){
index = self.selectedRows.findIndex(function(selectedRow){
return selectedRow == row;
});
if(index > -1){
row.getElement().classList.remove("tabulator-selected");
if(!row.modules.select){
row.modules.select = {};
}
row.modules.select.selected = false;
if(row.modules.select.checkboxEl){
row.modules.select.checkboxEl.checked = false;
}
self.selectedRows.splice(index, 1);
if(this.table.options.dataTreeSelectPropagate){
this.childRowSelection(row, false);
}
this.dispatchExternal("rowDeselected", row.getComponent());
self._rowSelectionChanged(silent);
}
}else {
if(!silent){
console.warn("Deselection Error - No such row found, ignoring selection:" + rowInfo);
}
}
}
getSelectedData(){
var data = [];
this.selectedRows.forEach(function(row){
data.push(row.getData());
});
return data;
}
getSelectedRows(){
var rows = [];
this.selectedRows.forEach(function(row){
rows.push(row.getComponent());
});
return rows;
}
_rowSelectionChanged(silent){
if(this.headerCheckboxElement){
if(this.selectedRows.length === 0){
this.headerCheckboxElement.checked = false;
this.headerCheckboxElement.indeterminate = false;
} else if(this.table.rowManager.rows.length === this.selectedRows.length){
this.headerCheckboxElement.checked = true;
this.headerCheckboxElement.indeterminate = false;
} else {
this.headerCheckboxElement.indeterminate = true;
this.headerCheckboxElement.checked = false;
}
}
if(!silent){
this.dispatchExternal("rowSelectionChanged", this.getSelectedData(), this.getSelectedRows());
}
}
registerRowSelectCheckbox (row, element) {
if(!row._row.modules.select){
row._row.modules.select = {};
}
row._row.modules.select.checkboxEl = element;
}
registerHeaderSelectCheckbox (element) {
this.headerCheckboxElement = element;
}
childRowSelection(row, select){
var children = this.table.modules.dataTree.getChildren(row, true);
if(select){
for(let child of children){
this._selectRow(child, true);
}
}else {
for(let child of children){
this._deselectRow(child, true);
}
}
}
}
SelectRow.moduleName = "selectRow";
//sort numbers
function number$1(a, b, aRow, bRow, column, dir, params){
var alignEmptyValues = params.alignEmptyValues;
var decimal = params.decimalSeparator;
var thousand = params.thousandSeparator;
var emptyAlign = 0;
a = String(a);
b = String(b);
if(thousand){
a = a.split(thousand).join("");
b = b.split(thousand).join("");
}
if(decimal){
a = a.split(decimal).join(".");
b = b.split(decimal).join(".");
}
a = parseFloat(a);
b = parseFloat(b);
//handle non numeric values
if(isNaN(a)){
emptyAlign = isNaN(b) ? 0 : -1;
}else if(isNaN(b)){
emptyAlign = 1;
}else {
//compare valid values
return a - b;
}
//fix empty values in position
if((alignEmptyValues === "top" && dir === "desc") || (alignEmptyValues === "bottom" && dir === "asc")){
emptyAlign *= -1;
}
return emptyAlign;
}
//sort strings
function string(a, b, aRow, bRow, column, dir, params){
var alignEmptyValues = params.alignEmptyValues;
var emptyAlign = 0;
var locale;
//handle empty values
if(!a){
emptyAlign = !b ? 0 : -1;
}else if(!b){
emptyAlign = 1;
}else {
//compare valid values
switch(typeof params.locale){
case "boolean":
if(params.locale){
locale = this.langLocale();
}
break;
case "string":
locale = params.locale;
break;
}
return String(a).toLowerCase().localeCompare(String(b).toLowerCase(), locale);
}
//fix empty values in position
if((alignEmptyValues === "top" && dir === "desc") || (alignEmptyValues === "bottom" && dir === "asc")){
emptyAlign *= -1;
}
return emptyAlign;
}
//sort datetime
function datetime$2(a, b, aRow, bRow, column, dir, params){
var DT = window.DateTime || luxon.DateTime;
var format = params.format || "dd/MM/yyyy HH:mm:ss",
alignEmptyValues = params.alignEmptyValues,
emptyAlign = 0;
if(typeof DT != "undefined"){
if(!DT.isDateTime(a)){
if(format === "iso"){
a = DT.fromISO(String(a));
}else {
a = DT.fromFormat(String(a), format);
}
}
if(!DT.isDateTime(b)){
if(format === "iso"){
b = DT.fromISO(String(b));
}else {
b = DT.fromFormat(String(b), format);
}
}
if(!a.isValid){
emptyAlign = !b.isValid ? 0 : -1;
}else if(!b.isValid){
emptyAlign = 1;
}else {
//compare valid values
return a - b;
}
//fix empty values in position
if((alignEmptyValues === "top" && dir === "desc") || (alignEmptyValues === "bottom" && dir === "asc")){
emptyAlign *= -1;
}
return emptyAlign;
}else {
console.error("Sort Error - 'datetime' sorter is dependant on luxon.js");
}
}
//sort date
function date$1(a, b, aRow, bRow, column, dir, params){
if(!params.format){
params.format = "dd/MM/yyyy";
}
return datetime$2.call(this, a, b, aRow, bRow, column, dir, params);
}
//sort times
function time$1(a, b, aRow, bRow, column, dir, params){
if(!params.format){
params.format = "HH:mm";
}
return datetime$2.call(this, a, b, aRow, bRow, column, dir, params);
}
//sort booleans
function boolean(a, b, aRow, bRow, column, dir, params){
var el1 = a === true || a === "true" || a === "True" || a === 1 ? 1 : 0;
var el2 = b === true || b === "true" || b === "True" || b === 1 ? 1 : 0;
return el1 - el2;
}
//sort if element contains any data
function array(a, b, aRow, bRow, column, dir, params){
var type = params.type || "length",
alignEmptyValues = params.alignEmptyValues,
emptyAlign = 0;
function calc(value){
var result;
switch(type){
case "length":
result = value.length;
break;
case "sum":
result = value.reduce(function(c, d){
return c + d;
});
break;
case "max":
result = Math.max.apply(null, value) ;
break;
case "min":
result = Math.min.apply(null, value) ;
break;
case "avg":
result = value.reduce(function(c, d){
return c + d;
}) / value.length;
break;
}
return result;
}
//handle non array values
if(!Array.isArray(a)){
emptyAlign = !Array.isArray(b) ? 0 : -1;
}else if(!Array.isArray(b)){
emptyAlign = 1;
}else {
return calc(b) - calc(a);
}
//fix empty values in position
if((alignEmptyValues === "top" && dir === "desc") || (alignEmptyValues === "bottom" && dir === "asc")){
emptyAlign *= -1;
}
return emptyAlign;
}
//sort if element contains any data
function exists(a, b, aRow, bRow, column, dir, params){
var el1 = typeof a == "undefined" ? 0 : 1;
var el2 = typeof b == "undefined" ? 0 : 1;
return el1 - el2;
}
//sort alpha numeric strings
function alphanum(as, bs, aRow, bRow, column, dir, params){
var a, b, a1, b1, i= 0, L, rx = /(\d+)|(\D+)/g, rd = /\d/;
var alignEmptyValues = params.alignEmptyValues;
var emptyAlign = 0;
//handle empty values
if(!as && as!== 0){
emptyAlign = !bs && bs!== 0 ? 0 : -1;
}else if(!bs && bs!== 0){
emptyAlign = 1;
}else {
if(isFinite(as) && isFinite(bs)) return as - bs;
a = String(as).toLowerCase();
b = String(bs).toLowerCase();
if(a === b) return 0;
if(!(rd.test(a) && rd.test(b))) return a > b ? 1 : -1;
a = a.match(rx);
b = b.match(rx);
L = a.length > b.length ? b.length : a.length;
while(i < L){
a1= a[i];
b1= b[i++];
if(a1 !== b1){
if(isFinite(a1) && isFinite(b1)){
if(a1.charAt(0) === "0") a1 = "." + a1;
if(b1.charAt(0) === "0") b1 = "." + b1;
return a1 - b1;
}
else return a1 > b1 ? 1 : -1;
}
}
return a.length > b.length;
}
//fix empty values in position
if((alignEmptyValues === "top" && dir === "desc") || (alignEmptyValues === "bottom" && dir === "asc")){
emptyAlign *= -1;
}
return emptyAlign;
}
var defaultSorters = {
number:number$1,
string:string,
date:date$1,
time:time$1,
datetime:datetime$2,
boolean:boolean,
array:array,
exists:exists,
alphanum:alphanum
};
class Sort extends Module{
constructor(table){
super(table);
this.sortList = []; //holder current sort
this.changed = false; //has the sort changed since last render
this.registerTableOption("sortMode", "local"); //local or remote sorting
this.registerTableOption("initialSort", false); //initial sorting criteria
this.registerTableOption("columnHeaderSortMulti", true); //multiple or single column sorting
this.registerTableOption("sortOrderReverse", false); //reverse internal sort ordering
this.registerTableOption("headerSortElement", ""); //header sort element
this.registerTableOption("headerSortClickElement", "header"); //element which triggers sort when clicked
this.registerColumnOption("sorter");
this.registerColumnOption("sorterParams");
this.registerColumnOption("headerSort", true);
this.registerColumnOption("headerSortStartingDir");
this.registerColumnOption("headerSortTristate");
}
initialize(){
this.subscribe("column-layout", this.initializeColumn.bind(this));
this.subscribe("table-built", this.tableBuilt.bind(this));
this.registerDataHandler(this.sort.bind(this), 20);
this.registerTableFunction("setSort", this.userSetSort.bind(this));
this.registerTableFunction("getSorters", this.getSort.bind(this));
this.registerTableFunction("clearSort", this.clearSort.bind(this));
if(this.table.options.sortMode === "remote"){
this.subscribe("data-params", this.remoteSortParams.bind(this));
}
}
tableBuilt(){
if(this.table.options.initialSort){
this.setSort(this.table.options.initialSort);
}
}
remoteSortParams(data, config, silent, params){
var sorters = this.getSort();
sorters.forEach((item) => {
delete item.column;
});
params.sort = sorters;
return params;
}
///////////////////////////////////
///////// Table Functions /////////
///////////////////////////////////
userSetSort(sortList, dir){
this.setSort(sortList, dir);
// this.table.rowManager.sorterRefresh();
this.refreshSort();
}
clearSort(){
this.clear();
// this.table.rowManager.sorterRefresh();
this.refreshSort();
}
///////////////////////////////////
///////// Internal Logic //////////
///////////////////////////////////
//initialize column header for sorting
initializeColumn(column){
var sorter = false,
colEl,
arrowEl;
switch(typeof column.definition.sorter){
case "string":
if(Sort.sorters[column.definition.sorter]){
sorter = Sort.sorters[column.definition.sorter];
}else {
console.warn("Sort Error - No such sorter found: ", column.definition.sorter);
}
break;
case "function":
sorter = column.definition.sorter;
break;
}
column.modules.sort = {
sorter:sorter, dir:"none",
params:column.definition.sorterParams || {},
startingDir:column.definition.headerSortStartingDir || "asc",
tristate: column.definition.headerSortTristate,
};
if(column.definition.headerSort !== false){
colEl = column.getElement();
colEl.classList.add("tabulator-sortable");
arrowEl = document.createElement("div");
arrowEl.classList.add("tabulator-col-sorter");
switch(this.table.options.headerSortClickElement){
case "icon":
arrowEl.classList.add("tabulator-col-sorter-element");
break;
case "header":
colEl.classList.add("tabulator-col-sorter-element");
break;
default:
colEl.classList.add("tabulator-col-sorter-element");
break;
}
switch(this.table.options.headerSortElement){
case "function":
//do nothing
break;
case "object":
arrowEl.appendChild(this.table.options.headerSortElement);
break;
default:
arrowEl.innerHTML = this.table.options.headerSortElement;
}
//create sorter arrow
column.titleHolderElement.appendChild(arrowEl);
column.modules.sort.element = arrowEl;
this.setColumnHeaderSortIcon(column, "none");
//sort on click
(this.table.options.headerSortClickElement === "icon" ? arrowEl : colEl).addEventListener("click", (e) => {
var dir = "",
sorters=[],
match = false;
if(column.modules.sort){
if(column.modules.sort.tristate){
if(column.modules.sort.dir == "none"){
dir = column.modules.sort.startingDir;
}else {
if(column.modules.sort.dir == column.modules.sort.startingDir){
dir = column.modules.sort.dir == "asc" ? "desc" : "asc";
}else {
dir = "none";
}
}
}else {
switch(column.modules.sort.dir){
case "asc":
dir = "desc";
break;
case "desc":
dir = "asc";
break;
default:
dir = column.modules.sort.startingDir;
}
}
if (this.table.options.columnHeaderSortMulti && (e.shiftKey || e.ctrlKey)) {
sorters = this.getSort();
match = sorters.findIndex((sorter) => {
return sorter.field === column.getField();
});
if(match > -1){
sorters[match].dir = dir;
match = sorters.splice(match, 1)[0];
if(dir != "none"){
sorters.push(match);
}
}else {
if(dir != "none"){
sorters.push({column:column, dir:dir});
}
}
//add to existing sort
this.setSort(sorters);
}else {
if(dir == "none"){
this.clear();
}else {
//sort by column only
this.setSort(column, dir);
}
}
// this.table.rowManager.sorterRefresh(!this.sortList.length);
this.refreshSort();
}
});
}
}
refreshSort(){
if(this.table.options.sortMode === "remote"){
this.reloadData(null, false, false);
}else {
this.refreshData(true);
}
//TODO - Persist left position of row manager
// left = this.scrollLeft;
// this.scrollHorizontal(left);
}
//check if the sorters have changed since last use
hasChanged(){
var changed = this.changed;
this.changed = false;
return changed;
}
//return current sorters
getSort(){
var self = this,
sorters = [];
self.sortList.forEach(function(item){
if(item.column){
sorters.push({column:item.column.getComponent(), field:item.column.getField(), dir:item.dir});
}
});
return sorters;
}
//change sort list and trigger sort
setSort(sortList, dir){
var self = this,
newSortList = [];
if(!Array.isArray(sortList)){
sortList = [{column: sortList, dir:dir}];
}
sortList.forEach(function(item){
var column;
column = self.table.columnManager.findColumn(item.column);
if(column){
item.column = column;
newSortList.push(item);
self.changed = true;
}else {
console.warn("Sort Warning - Sort field does not exist and is being ignored: ", item.column);
}
});
self.sortList = newSortList;
this.dispatch("sort-changed");
}
//clear sorters
clear(){
this.setSort([]);
}
//find appropriate sorter for column
findSorter(column){
var row = this.table.rowManager.activeRows[0],
sorter = "string",
field, value;
if(row){
row = row.getData();
field = column.getField();
if(field){
value = column.getFieldValue(row);
switch(typeof value){
case "undefined":
sorter = "string";
break;
case "boolean":
sorter = "boolean";
break;
default:
if(!isNaN(value) && value !== ""){
sorter = "number";
}else {
if(value.match(/((^[0-9]+[a-z]+)|(^[a-z]+[0-9]+))+$/i)){
sorter = "alphanum";
}
}
break;
}
}
}
return Sort.sorters[sorter];
}
//work through sort list sorting data
sort(data){
var self = this,
sortList = this.table.options.sortOrderReverse ? self.sortList.slice().reverse() : self.sortList,
sortListActual = [],
rowComponents = [];
if(this.subscribedExternal("dataSorting")){
this.dispatchExternal("dataSorting", self.getSort());
}
self.clearColumnHeaders();
if(this.table.options.sortMode !== "remote"){
//build list of valid sorters and trigger column specific callbacks before sort begins
sortList.forEach(function(item, i){
var sortObj;
if(item.column){
sortObj = item.column.modules.sort;
if(sortObj){
//if no sorter has been defined, take a guess
if(!sortObj.sorter){
sortObj.sorter = self.findSorter(item.column);
}
item.params = typeof sortObj.params === "function" ? sortObj.params(item.column.getComponent(), item.dir) : sortObj.params;
sortListActual.push(item);
}
self.setColumnHeader(item.column, item.dir);
}
});
//sort data
if (sortListActual.length) {
self._sortItems(data, sortListActual);
}
}else {
sortList.forEach(function(item, i){
self.setColumnHeader(item.column, item.dir);
});
}
if(this.subscribedExternal("dataSorted")){
data.forEach((row) => {
rowComponents.push(row.getComponent());
});
this.dispatchExternal("dataSorted", self.getSort(), rowComponents);
}
return data;
}
//clear sort arrows on columns
clearColumnHeaders(){
this.table.columnManager.getRealColumns().forEach((column) => {
if(column.modules.sort){
column.modules.sort.dir = "none";
column.getElement().setAttribute("aria-sort", "none");
this.setColumnHeaderSortIcon(column, "none");
}
});
}
//set the column header sort direction
setColumnHeader(column, dir){
column.modules.sort.dir = dir;
column.getElement().setAttribute("aria-sort", dir === "asc" ? "ascending" : "descending");
this.setColumnHeaderSortIcon(column, dir);
}
setColumnHeaderSortIcon(column, dir){
var sortEl = column.modules.sort.element,
arrowEl;
if(column.definition.headerSort && typeof this.table.options.headerSortElement === "function"){
while(sortEl.firstChild) sortEl.removeChild(sortEl.firstChild);
arrowEl = this.table.options.headerSortElement.call(this.table, column.getComponent(), dir);
if(typeof arrowEl === "object"){
sortEl.appendChild(arrowEl);
}else {
sortEl.innerHTML = arrowEl;
}
}
}
//sort each item in sort list
_sortItems(data, sortList){
var sorterCount = sortList.length - 1;
data.sort((a, b) => {
var result;
for(var i = sorterCount; i>= 0; i--){
let sortItem = sortList[i];
result = this._sortRow(a, b, sortItem.column, sortItem.dir, sortItem.params);
if(result !== 0){
break;
}
}
return result;
});
}
//process individual rows for a sort function on active data
_sortRow(a, b, column, dir, params){
var el1Comp, el2Comp;
//switch elements depending on search direction
var el1 = dir == "asc" ? a : b;
var el2 = dir == "asc" ? b : a;
a = column.getFieldValue(el1.getData());
b = column.getFieldValue(el2.getData());
a = typeof a !== "undefined" ? a : "";
b = typeof b !== "undefined" ? b : "";
el1Comp = el1.getComponent();
el2Comp = el2.getComponent();
return column.modules.sort.sorter.call(this, a, b, el1Comp, el2Comp, column.getComponent(), dir, params);
}
}
Sort.moduleName = "sort";
//load defaults
Sort.sorters = defaultSorters;
class Tooltip extends Module{
constructor(table){
super(table);
this.tooltipSubscriber = null,
this.headerSubscriber = null,
this.timeout = null;
this.popupInstance = null;
this.registerTableOption("tooltipGenerationMode", undefined); //deprecated
this.registerTableOption("tooltipDelay", 300);
this.registerColumnOption("tooltip");
this.registerColumnOption("headerTooltip");
}
initialize(){
this.deprecatedOptionsCheck();
this.subscribe("column-init", this.initializeColumn.bind(this));
}
deprecatedOptionsCheck(){
this.deprecationCheckMsg("tooltipGenerationMode", "This option is no longer needed as tooltips are always generated on hover now");
}
initializeColumn(column){
if(column.definition.headerTooltip && !this.headerSubscriber){
this.headerSubscriber = true;
this.subscribe("column-mousemove", this.mousemoveCheck.bind(this, "headerTooltip"));
this.subscribe("column-mouseout", this.mouseoutCheck.bind(this, "headerTooltip"));
}
if(column.definition.tooltip && !this.tooltipSubscriber){
this.tooltipSubscriber = true;
this.subscribe("cell-mousemove", this.mousemoveCheck.bind(this, "tooltip"));
this.subscribe("cell-mouseout", this.mouseoutCheck.bind(this, "tooltip"));
}
}
mousemoveCheck(action, e, component){
var tooltip = action === "tooltip" ? component.column.definition.tooltip : component.definition.headerTooltip;
if(tooltip){
this.clearPopup();
this.timeout = setTimeout(this.loadTooltip.bind(this, e, component, tooltip), this.table.options.tooltipDelay);
}
}
mouseoutCheck(action, e, component){
if(!this.popupInstance){
this.clearPopup();
}
}
clearPopup(action, e, component){
clearTimeout(this.timeout);
this.timeout = null;
if(this.popupInstance){
this.popupInstance.hide();
}
}
loadTooltip(e, component, tooltip){
var contentsEl, renderedCallback, coords;
function onRendered(callback){
renderedCallback = callback;
}
if(typeof tooltip === "function"){
tooltip = tooltip(e, component.getComponent(), onRendered);
}
if(tooltip instanceof HTMLElement){
contentsEl = tooltip;
}else {
contentsEl = document.createElement("div");
if(tooltip === true){
if(component instanceof Cell){
tooltip = component.value;
}else {
if(component.definition.field){
this.langBind("columns|" + component.definition.field, (value) => {
contentsEl.innerHTML = tooltip = value || component.definition.title;
});
}else {
tooltip = component.definition.title;
}
}
}
contentsEl.innerHTML = tooltip;
}
if(tooltip || tooltip === 0 || tooltip === false){
contentsEl.classList.add("tabulator-tooltip");
contentsEl.addEventListener("mousemove", e => e.preventDefault());
this.popupInstance = this.popup(contentsEl);
if(typeof renderedCallback === "function"){
this.popupInstance.renderCallback(renderedCallback);
}
coords = this.popupInstance.containerEventCoords(e);
this.popupInstance.show(coords.x + 15, coords.y + 15).hideOnBlur(() => {
this.dispatchExternal("TooltipClosed", component.getComponent());
this.popupInstance = null;
});
this.dispatchExternal("TooltipOpened", component.getComponent());
}
}
}
Tooltip.moduleName = "tooltip";
var defaultValidators = {
//is integer
integer: function(cell, value, parameters){
if(value === "" || value === null || typeof value === "undefined"){
return true;
}
value = Number(value);
return !isNaN(value) && isFinite(value) && Math.floor(value) === value;
},
//is float
float: function(cell, value, parameters){
if(value === "" || value === null || typeof value === "undefined"){
return true;
}
value = Number(value);
return !isNaN(value) && isFinite(value) && value % 1 !== 0;
},
//must be a number
numeric: function(cell, value, parameters){
if(value === "" || value === null || typeof value === "undefined"){
return true;
}
return !isNaN(value);
},
//must be a string
string: function(cell, value, parameters){
if(value === "" || value === null || typeof value === "undefined"){
return true;
}
return isNaN(value);
},
//maximum value
max: function(cell, value, parameters){
if(value === "" || value === null || typeof value === "undefined"){
return true;
}
return parseFloat(value) <= parameters;
},
//minimum value
min: function(cell, value, parameters){
if(value === "" || value === null || typeof value === "undefined"){
return true;
}
return parseFloat(value) >= parameters;
},
//starts with value
starts: function(cell, value, parameters){
if(value === "" || value === null || typeof value === "undefined"){
return true;
}
return String(value).toLowerCase().startsWith(String(parameters).toLowerCase());
},
//ends with value
ends: function(cell, value, parameters){
if(value === "" || value === null || typeof value === "undefined"){
return true;
}
return String(value).toLowerCase().endsWith(String(parameters).toLowerCase());
},
//minimum string length
minLength: function(cell, value, parameters){
if(value === "" || value === null || typeof value === "undefined"){
return true;
}
return String(value).length >= parameters;
},
//maximum string length
maxLength: function(cell, value, parameters){
if(value === "" || value === null || typeof value === "undefined"){
return true;
}
return String(value).length <= parameters;
},
//in provided value list
in: function(cell, value, parameters){
if(value === "" || value === null || typeof value === "undefined"){
return true;
}
if(typeof parameters == "string"){
parameters = parameters.split("|");
}
return parameters.indexOf(value) > -1;
},
//must match provided regex
regex: function(cell, value, parameters){
if(value === "" || value === null || typeof value === "undefined"){
return true;
}
var reg = new RegExp(parameters);
return reg.test(value);
},
//value must be unique in this column
unique: function(cell, value, parameters){
if(value === "" || value === null || typeof value === "undefined"){
return true;
}
var unique = true;
var cellData = cell.getData();
var column = cell.getColumn()._getSelf();
this.table.rowManager.rows.forEach(function(row){
var data = row.getData();
if(data !== cellData){
if(value == column.getFieldValue(data)){
unique = false;
}
}
});
return unique;
},
//must have a value
required:function(cell, value, parameters){
return value !== "" && value !== null && typeof value !== "undefined";
},
};
class Validate extends Module{
constructor(table){
super(table);
this.invalidCells = [];
this.registerTableOption("validationMode", "blocking");
this.registerColumnOption("validator");
this.registerTableFunction("getInvalidCells", this.getInvalidCells.bind(this));
this.registerTableFunction("clearCellValidation", this.userClearCellValidation.bind(this));
this.registerTableFunction("validate", this.userValidate.bind(this));
this.registerComponentFunction("cell", "isValid", this.cellIsValid.bind(this));
this.registerComponentFunction("cell", "clearValidation", this.clearValidation.bind(this));
this.registerComponentFunction("cell", "validate", this.cellValidate.bind(this));
this.registerComponentFunction("column", "validate", this.columnValidate.bind(this));
this.registerComponentFunction("row", "validate", this.rowValidate.bind(this));
}
initialize(){
this.subscribe("cell-delete", this.clearValidation.bind(this));
this.subscribe("column-layout", this.initializeColumnCheck.bind(this));
this.subscribe("edit-success", this.editValidate.bind(this));
this.subscribe("edit-editor-clear", this.editorClear.bind(this));
this.subscribe("edit-edited-clear", this.editedClear.bind(this));
}
///////////////////////////////////
///////// Event Handling //////////
///////////////////////////////////
editValidate(cell, value, previousValue){
var valid = this.table.options.validationMode !== "manual" ? this.validate(cell.column.modules.validate, cell, value) : true;
// allow time for editor to make render changes then style cell
if(valid !== true){
setTimeout(() => {
cell.getElement().classList.add("tabulator-validation-fail");
this.dispatchExternal("validationFailed", cell.getComponent(), value, valid);
});
}
return valid;
}
editorClear(cell, cancelled){
if(cancelled){
if(cell.column.modules.validate){
this.cellValidate(cell);
}
}
cell.getElement().classList.remove("tabulator-validation-fail");
}
editedClear(cell){
if(cell.modules.validate){
cell.modules.validate.invalid = false;
}
}
///////////////////////////////////
////////// Cell Functions /////////
///////////////////////////////////
cellIsValid(cell){
return cell.modules.validate ? (cell.modules.validate.invalid || true) : true;
}
cellValidate(cell){
return this.validate(cell.column.modules.validate, cell, cell.getValue());
}
///////////////////////////////////
///////// Column Functions ////////
///////////////////////////////////
columnValidate(column){
var invalid = [];
column.cells.forEach((cell) => {
if(this.cellValidate(cell) !== true){
invalid.push(cell.getComponent());
}
});
return invalid.length ? invalid : true;
}
///////////////////////////////////
////////// Row Functions //////////
///////////////////////////////////
rowValidate(row){
var invalid = [];
row.cells.forEach((cell) => {
if(this.cellValidate(cell) !== true){
invalid.push(cell.getComponent());
}
});
return invalid.length ? invalid : true;
}
///////////////////////////////////
///////// Table Functions /////////
///////////////////////////////////
userClearCellValidation(cells){
if(!cells){
cells = this.getInvalidCells();
}
if(!Array.isArray(cells)){
cells = [cells];
}
cells.forEach((cell) => {
this.clearValidation(cell._getSelf());
});
}
userValidate(cells){
var output = [];
//clear row data
this.table.rowManager.rows.forEach((row) => {
row = row.getComponent();
var valid = row.validate();
if(valid !== true){
output = output.concat(valid);
}
});
return output.length ? output : true;
}
///////////////////////////////////
///////// Internal Logic //////////
///////////////////////////////////
initializeColumnCheck(column){
if(typeof column.definition.validator !== "undefined"){
this.initializeColumn(column);
}
}
//validate
initializeColumn(column){
var self = this,
config = [],
validator;
if(column.definition.validator){
if(Array.isArray(column.definition.validator)){
column.definition.validator.forEach((item) => {
validator = self._extractValidator(item);
if(validator){
config.push(validator);
}
});
}else {
validator = this._extractValidator(column.definition.validator);
if(validator){
config.push(validator);
}
}
column.modules.validate = config.length ? config : false;
}
}
_extractValidator(value){
var type, params, pos;
switch(typeof value){
case "string":
pos = value.indexOf(':');
if(pos > -1){
type = value.substring(0,pos);
params = value.substring(pos+1);
}else {
type = value;
}
return this._buildValidator(type, params);
case "function":
return this._buildValidator(value);
case "object":
return this._buildValidator(value.type, value.parameters);
}
}
_buildValidator(type, params){
var func = typeof type == "function" ? type : Validate.validators[type];
if(!func){
console.warn("Validator Setup Error - No matching validator found:", type);
return false;
}else {
return {
type:typeof type == "function" ? "function" : type,
func:func,
params:params,
};
}
}
validate(validators, cell, value){
var self = this,
failedValidators = [],
invalidIndex = this.invalidCells.indexOf(cell);
if(validators){
validators.forEach((item) => {
if(!item.func.call(self, cell.getComponent(), value, item.params)){
failedValidators.push({
type:item.type,
parameters:item.params
});
}
});
}
if(!cell.modules.validate){
cell.modules.validate = {};
}
if(!failedValidators.length){
cell.modules.validate.invalid = false;
cell.getElement().classList.remove("tabulator-validation-fail");
if(invalidIndex > -1){
this.invalidCells.splice(invalidIndex, 1);
}
}else {
cell.modules.validate.invalid = failedValidators;
if(this.table.options.validationMode !== "manual"){
cell.getElement().classList.add("tabulator-validation-fail");
}
if(invalidIndex == -1){
this.invalidCells.push(cell);
}
}
return failedValidators.length ? failedValidators : true;
}
getInvalidCells(){
var output = [];
this.invalidCells.forEach((cell) => {
output.push(cell.getComponent());
});
return output;
}
clearValidation(cell){
var invalidIndex;
if(cell.modules.validate && cell.modules.validate.invalid){
cell.getElement().classList.remove("tabulator-validation-fail");
cell.modules.validate.invalid = false;
invalidIndex = this.invalidCells.indexOf(cell);
if(invalidIndex > -1){
this.invalidCells.splice(invalidIndex, 1);
}
}
}
}
Validate.moduleName = "validate";
//load defaults
Validate.validators = defaultValidators;
var modules = /*#__PURE__*/Object.freeze({
__proto__: null,
AccessorModule: Accessor,
AjaxModule: Ajax,
ClipboardModule: Clipboard,
ColumnCalcsModule: ColumnCalcs,
DataTreeModule: DataTree,
DownloadModule: Download,
EditModule: Edit$1,
ExportModule: Export,
FilterModule: Filter,
FormatModule: Format,
FrozenColumnsModule: FrozenColumns,
FrozenRowsModule: FrozenRows,
GroupRowsModule: GroupRows,
HistoryModule: History,
HtmlTableImportModule: HtmlTableImport,
ImportModule: Import,
InteractionModule: Interaction,
KeybindingsModule: Keybindings,
MenuModule: Menu,
MoveColumnsModule: MoveColumns,
MoveRowsModule: MoveRows,
MutatorModule: Mutator,
PageModule: Page,
PersistenceModule: Persistence,
PopupModule: Popup$1,
PrintModule: Print,
ReactiveDataModule: ReactiveData,
ResizeColumnsModule: ResizeColumns,
ResizeRowsModule: ResizeRows,
ResizeTableModule: ResizeTable,
ResponsiveLayoutModule: ResponsiveLayout,
SelectRowModule: SelectRow,
SortModule: Sort,
TooltipModule: Tooltip,
ValidateModule: Validate
});
var defaultOptions = {
debugEventsExternal:false, //flag to console log events
debugEventsInternal:false, //flag to console log events
debugInvalidOptions:true, //allow toggling of invalid option warnings
debugInvalidComponentFuncs:true, //allow toggling of invalid component warnings
debugInitialization:true, //allow toggling of pre initialization function call warnings
debugDeprecation:true, //allow toggling of deprecation warnings
height:false, //height of tabulator
minHeight:false, //minimum height of tabulator
maxHeight:false, //maximum height of tabulator
columnHeaderVertAlign:"top", //vertical alignment of column headers
popupContainer:false,
columns:[],//store for colum header info
columnDefaults:{}, //store column default props
data:false, //default starting data
autoColumns:false, //build columns from data row structure
autoColumnsDefinitions:false,
nestedFieldSeparator:".", //separator for nested data
footerElement:false, //hold footer element
index:"id", //filed for row index
textDirection:"auto",
addRowPos:"bottom", //position to insert blank rows, top|bottom
headerVisible:true, //hide header
renderVertical:"virtual",
renderHorizontal:"basic",
renderVerticalBuffer:0, // set virtual DOM buffer size
scrollToRowPosition:"top",
scrollToRowIfVisible:true,
scrollToColumnPosition:"left",
scrollToColumnIfVisible:true,
rowFormatter:false,
rowFormatterPrint:null,
rowFormatterClipboard:null,
rowFormatterHtmlOutput:null,
rowHeight:null,
placeholder:false,
dataLoader:true,
dataLoaderLoading:false,
dataLoaderError:false,
dataLoaderErrorTimeout:3000,
dataSendParams:{},
dataReceiveParams:{},
};
class OptionsList {
constructor(table, msgType, defaults = {}){
this.table = table;
this.msgType = msgType;
this.registeredDefaults = Object.assign({}, defaults);
}
register(option, value){
this.registeredDefaults[option] = value;
}
generate(defaultOptions, userOptions = {}){
var output = Object.assign({}, this.registeredDefaults),
warn = this.table.options.debugInvalidOptions || userOptions.debugInvalidOptions === true;
Object.assign(output, defaultOptions);
for (let key in userOptions){
if(!output.hasOwnProperty(key)){
if(warn){
console.warn("Invalid " + this.msgType + " option:", key);
}
output[key] = userOptions.key;
}
}
for (let key in output){
if(key in userOptions){
output[key] = userOptions[key];
}else {
if(Array.isArray(output[key])){
output[key] = Object.assign([], output[key]);
}else if(typeof output[key] === "object" && output[key] !== null){
output[key] = Object.assign({}, output[key]);
}else if (typeof output[key] === "undefined"){
delete output[key];
}
}
}
return output;
}
}
class Renderer extends CoreFeature{
constructor(table){
super(table);
this.elementVertical = table.rowManager.element;
this.elementHorizontal = table.columnManager.element;
this.tableElement = table.rowManager.tableElement;
this.verticalFillMode = "fit"; // used by row manager to determine how to size the render area ("fit" - fits container to the contents, "fill" - fills the container without resizing it)
}
///////////////////////////////////
/////// Internal Bindings /////////
///////////////////////////////////
initialize(){
//initialize core functionality
}
clearRows(){
//clear down existing rows layout
}
clearColumns(){
//clear down existing columns layout
}
reinitializeColumnWidths(columns){
//resize columns to fit data
}
renderRows(){
//render rows from a clean slate
}
renderColumns(){
//render columns from a clean slate
}
rerenderRows(callback){
// rerender rows and keep position
if(callback){
callback();
}
}
rerenderColumns(update, blockRedraw){
//rerender columns
}
renderRowCells(row){
//render the cells in a row
}
rerenderRowCells(row, force){
//rerender the cells in a row
}
scrollColumns(left, dir){
//handle horizontal scrolling
}
scrollRows(top, dir){
//handle vertical scrolling
}
resize(){
//container has resized, carry out any needed recalculations (DO NOT RERENDER IN THIS FUNCTION)
}
scrollToRow(row){
//scroll to a specific row
}
scrollToRowNearestTop(row){
//determine weather the row is nearest the top or bottom of the table, return true for top or false for bottom
}
visibleRows(includingBuffer){
//return the visible rows
return [];
}
///////////////////////////////////
//////// Helper Functions /////////
///////////////////////////////////
rows(){
return this.table.rowManager.getDisplayRows();
}
styleRow(row, index){
var rowEl = row.getElement();
if(index % 2){
rowEl.classList.add("tabulator-row-even");
rowEl.classList.remove("tabulator-row-odd");
}else {
rowEl.classList.add("tabulator-row-odd");
rowEl.classList.remove("tabulator-row-even");
}
}
///////////////////////////////////
/////// External Triggers /////////
/////// (DO NOT OVERRIDE) /////////
///////////////////////////////////
clear(){
//clear down existing layout
this.clearRows();
this.clearColumns();
}
render(){
//render from a clean slate
this.renderRows();
this.renderColumns();
}
rerender(callback){
// rerender and keep position
this.rerenderRows();
this.rerenderColumns();
}
scrollToRowPosition(row, position, ifVisible){
var rowIndex = this.rows().indexOf(row),
rowEl = row.getElement(),
offset = 0;
return new Promise((resolve, reject) => {
if(rowIndex > -1){
if(typeof ifVisible === "undefined"){
ifVisible = this.table.options.scrollToRowIfVisible;
}
//check row visibility
if(!ifVisible){
if(Helpers.elVisible(rowEl)){
offset = Helpers.elOffset(rowEl).top - Helpers.elOffset(this.elementVertical).top;
if(offset > 0 && offset < this.elementVertical.clientHeight - rowEl.offsetHeight){
resolve();
return false;
}
}
}
if(typeof position === "undefined"){
position = this.table.options.scrollToRowPosition;
}
if(position === "nearest"){
position = this.scrollToRowNearestTop(row) ? "top" : "bottom";
}
//scroll to row
this.scrollToRow(row);
//align to correct position
switch(position){
case "middle":
case "center":
if(this.elementVertical.scrollHeight - this.elementVertical.scrollTop == this.elementVertical.clientHeight){
this.elementVertical.scrollTop = this.elementVertical.scrollTop + (rowEl.offsetTop - this.elementVertical.scrollTop) - ((this.elementVertical.scrollHeight - rowEl.offsetTop) / 2);
}else {
this.elementVertical.scrollTop = this.elementVertical.scrollTop - (this.elementVertical.clientHeight / 2);
}
break;
case "bottom":
if(this.elementVertical.scrollHeight - this.elementVertical.scrollTop == this.elementVertical.clientHeight){
this.elementVertical.scrollTop = this.elementVertical.scrollTop - (this.elementVertical.scrollHeight - rowEl.offsetTop) + rowEl.offsetHeight;
}else {
this.elementVertical.scrollTop = this.elementVertical.scrollTop - this.elementVertical.clientHeight + rowEl.offsetHeight;
}
break;
case "top":
this.elementVertical.scrollTop = rowEl.offsetTop;
break;
}
resolve();
}else {
console.warn("Scroll Error - Row not visible");
reject("Scroll Error - Row not visible");
}
});
}
}
class BasicHorizontal extends Renderer{
constructor(table){
super(table);
}
renderRowCells(row){
row.cells.forEach((cell) => {
row.element.appendChild(cell.getElement());
cell.cellRendered();
});
}
reinitializeColumnWidths(columns){
columns.forEach(function(column){
column.reinitializeWidth();
});
}
}
class VirtualDomHorizontal extends Renderer{
constructor(table){
super(table);
this.leftCol = 0;
this.rightCol = 0;
this.scrollLeft = 0;
this.vDomScrollPosLeft = 0;
this.vDomScrollPosRight = 0;
this.vDomPadLeft = 0;
this.vDomPadRight = 0;
this.fitDataColAvg = 0;
this.windowBuffer = 200; //pixel margin to make column visible before it is shown on screen
this.visibleRows = null;
this.initialized = false;
this.isFitData = false;
this.columns = [];
}
initialize(){
this.compatibilityCheck();
this.layoutCheck();
this.vertScrollListen();
}
compatibilityCheck(){
if(this.options("layout") == "fitDataTable"){
console.warn("Horizontal Virtual DOM is not compatible with fitDataTable layout mode");
}
if(this.options("responsiveLayout")){
console.warn("Horizontal Virtual DOM is not compatible with responsive columns");
}
if(this.options("rtl")){
console.warn("Horizontal Virtual DOM is not currently compatible with RTL text direction");
}
}
layoutCheck(){
this.isFitData = this.options("layout").startsWith('fitData');
}
vertScrollListen(){
this.subscribe("scroll-vertical", this.clearVisRowCache.bind(this));
this.subscribe("data-refreshed", this.clearVisRowCache.bind(this));
}
clearVisRowCache(){
this.visibleRows = null;
}
//////////////////////////////////////
///////// Public Functions ///////////
//////////////////////////////////////
renderColumns(row, force){
this.dataChange();
}
scrollColumns(left, dir){
if(this.scrollLeft != left){
this.scrollLeft = left;
this.scroll(left - (this.vDomScrollPosLeft + this.windowBuffer));
}
}
calcWindowBuffer(){
var buffer = this.elementVertical.clientWidth;
this.table.columnManager.columnsByIndex.forEach((column) => {
if(column.visible){
var width = column.getWidth();
if(width > buffer){
buffer = width;
}
}
});
this.windowBuffer = buffer * 2;
}
rerenderColumns(update, blockRedraw){
var old = {
cols:this.columns,
leftCol:this.leftCol,
rightCol:this.rightCol,
},
colPos = 0;
if(update && !this.initialized){
return;
}
this.clear();
this.calcWindowBuffer();
this.scrollLeft = this.elementVertical.scrollLeft;
this.vDomScrollPosLeft = this.scrollLeft - this.windowBuffer;
this.vDomScrollPosRight = this.scrollLeft + this.elementVertical.clientWidth + this.windowBuffer;
this.table.columnManager.columnsByIndex.forEach((column) => {
var config = {},
width;
if(column.visible){
if(!column.modules.frozen){
width = column.getWidth();
config.leftPos = colPos;
config.rightPos = colPos + width;
config.width = width;
if (this.isFitData) {
config.fitDataCheck = column.modules.vdomHoz ? column.modules.vdomHoz.fitDataCheck : true;
}
if((colPos + width > this.vDomScrollPosLeft) && (colPos < this.vDomScrollPosRight)){
//column is visible
if(this.leftCol == -1){
this.leftCol = this.columns.length;
this.vDomPadLeft = colPos;
}
this.rightCol = this.columns.length;
}else {
// column is hidden
if(this.leftCol !== -1){
this.vDomPadRight += width;
}
}
this.columns.push(column);
column.modules.vdomHoz = config;
colPos += width;
}
}
});
this.tableElement.style.paddingLeft = this.vDomPadLeft + "px";
this.tableElement.style.paddingRight = this.vDomPadRight + "px";
this.initialized = true;
if(!blockRedraw){
if(!update || this.reinitChanged(old)){
this.reinitializeRows();
}
}
this.elementVertical.scrollLeft = this.scrollLeft;
}
renderRowCells(row){
if(this.initialized){
this.initializeRow(row);
}else {
row.cells.forEach((cell) => {
row.element.appendChild(cell.getElement());
cell.cellRendered();
});
}
}
rerenderRowCells(row, force){
this.reinitializeRow(row, force);
}
reinitializeColumnWidths(columns){
for(let i = this.leftCol; i <= this.rightCol; i++){
this.columns[i].reinitializeWidth();
}
}
//////////////////////////////////////
//////// Internal Rendering //////////
//////////////////////////////////////
deinitialize(){
this.initialized = false;
}
clear(){
this.columns = [];
this.leftCol = -1;
this.rightCol = 0;
this.vDomScrollPosLeft = 0;
this.vDomScrollPosRight = 0;
this.vDomPadLeft = 0;
this.vDomPadRight = 0;
}
dataChange(){
var change = false,
row, rowEl;
if(this.isFitData){
this.table.columnManager.columnsByIndex.forEach((column) => {
if(!column.definition.width && column.visible){
change = true;
}
});
if(change && this.table.rowManager.getDisplayRows().length){
this.vDomScrollPosRight = this.scrollLeft + this.elementVertical.clientWidth + this.windowBuffer;
row = this.chain("rows-sample", [1], [], () => {
return this.table.rowManager.getDisplayRows();
})[0];
if(row){
rowEl = row.getElement();
row.generateCells();
this.tableElement.appendChild(rowEl);
for(let colEnd = 0; colEnd < row.cells.length; colEnd++){
let cell = row.cells[colEnd];
rowEl.appendChild(cell.getElement());
cell.column.reinitializeWidth();
}
rowEl.parentNode.removeChild(rowEl);
this.rerenderColumns(false, true);
}
}
}else {
if(this.options("layout") === "fitColumns"){
this.layoutRefresh();
this.rerenderColumns(false, true);
}
}
}
reinitChanged(old){
var match = true;
if(old.cols.length !== this.columns.length || old.leftCol !== this.leftCol || old.rightCol !== this.rightCol){
return true;
}
old.cols.forEach((col, i) => {
if(col !== this.columns[i]){
match = false;
}
});
return !match;
}
reinitializeRows(){
var visibleRows = this.getVisibleRows(),
otherRows = this.table.rowManager.getRows().filter(row => !visibleRows.includes(row));
visibleRows.forEach((row) => {
this.reinitializeRow(row, true);
});
otherRows.forEach((row) =>{
row.deinitialize();
});
}
getVisibleRows(){
if (!this.visibleRows){
this.visibleRows = this.table.rowManager.getVisibleRows();
}
return this.visibleRows;
}
scroll(diff){
this.vDomScrollPosLeft += diff;
this.vDomScrollPosRight += diff;
if(Math.abs(diff) > (this.windowBuffer / 2)){
this.rerenderColumns();
}else {
if(diff > 0){
//scroll right
this.addColRight();
this.removeColLeft();
}else {
//scroll left
this.addColLeft();
this.removeColRight();
}
}
}
colPositionAdjust (start, end, diff){
for(let i = start; i < end; i++){
let column = this.columns[i];
column.modules.vdomHoz.leftPos += diff;
column.modules.vdomHoz.rightPos += diff;
}
}
addColRight(){
var changes = false,
working = true;
while(working){
let column = this.columns[this.rightCol + 1];
if(column){
if(column.modules.vdomHoz.leftPos <= this.vDomScrollPosRight){
changes = true;
this.getVisibleRows().forEach((row) => {
if(row.type !== "group"){
var cell = row.getCell(column);
row.getElement().insertBefore(cell.getElement(), row.getCell(this.columns[this.rightCol]).getElement().nextSibling);
cell.cellRendered();
}
});
this.fitDataColActualWidthCheck(column);
this.rightCol++; // Don't move this below the >= check below
this.getVisibleRows().forEach((row) => {
if(row.type !== "group"){
row.modules.vdomHoz.rightCol = this.rightCol;
}
});
if(this.rightCol >= (this.columns.length - 1)){
this.vDomPadRight = 0;
}else {
this.vDomPadRight -= column.getWidth();
}
}else {
working = false;
}
}else {
working = false;
}
}
if(changes){
this.tableElement.style.paddingRight = this.vDomPadRight + "px";
}
}
addColLeft(){
var changes = false,
working = true;
while(working){
let column = this.columns[this.leftCol - 1];
if(column){
if(column.modules.vdomHoz.rightPos >= this.vDomScrollPosLeft){
changes = true;
this.getVisibleRows().forEach((row) => {
if(row.type !== "group"){
var cell = row.getCell(column);
row.getElement().insertBefore(cell.getElement(), row.getCell(this.columns[this.leftCol]).getElement());
cell.cellRendered();
}
});
this.leftCol--; // don't move this below the <= check below
this.getVisibleRows().forEach((row) => {
if(row.type !== "group"){
row.modules.vdomHoz.leftCol = this.leftCol;
}
});
if(this.leftCol <= 0){ // replicating logic in addColRight
this.vDomPadLeft = 0;
}else {
this.vDomPadLeft -= column.getWidth();
}
let diff = this.fitDataColActualWidthCheck(column);
if(diff){
this.scrollLeft = this.elementVertical.scrollLeft = this.elementVertical.scrollLeft + diff;
this.vDomPadRight -= diff;
}
}else {
working = false;
}
}else {
working = false;
}
}
if(changes){
this.tableElement.style.paddingLeft = this.vDomPadLeft + "px";
}
}
removeColRight(){
var changes = false,
working = true;
while(working){
let column = this.columns[this.rightCol];
if(column){
if(column.modules.vdomHoz.leftPos > this.vDomScrollPosRight){
changes = true;
this.getVisibleRows().forEach((row) => {
if(row.type !== "group"){
var cell = row.getCell(column);
try {
row.getElement().removeChild(cell.getElement());
} catch (ex) {
console.warn("Could not removeColRight", ex.message);
}
}
});
this.vDomPadRight += column.getWidth();
this.rightCol --;
this.getVisibleRows().forEach((row) => {
if(row.type !== "group"){
row.modules.vdomHoz.rightCol = this.rightCol;
}
});
}else {
working = false;
}
}else {
working = false;
}
}
if(changes){
this.tableElement.style.paddingRight = this.vDomPadRight + "px";
}
}
removeColLeft(){
var changes = false,
working = true;
while(working){
let column = this.columns[this.leftCol];
if(column){
if(column.modules.vdomHoz.rightPos < this.vDomScrollPosLeft){
changes = true;
this.getVisibleRows().forEach((row) => {
if(row.type !== "group"){
var cell = row.getCell(column);
try {
row.getElement().removeChild(cell.getElement());
} catch (ex) {
console.warn("Could not removeColLeft", ex.message);
}
}
});
this.vDomPadLeft += column.getWidth();
this.leftCol ++;
this.getVisibleRows().forEach((row) => {
if(row.type !== "group"){
row.modules.vdomHoz.leftCol = this.leftCol;
}
});
}else {
working = false;
}
}else {
working = false;
}
}
if(changes){
this.tableElement.style.paddingLeft = this.vDomPadLeft + "px";
}
}
fitDataColActualWidthCheck(column){
var newWidth, widthDiff;
if(column.modules.vdomHoz.fitDataCheck){
column.reinitializeWidth();
newWidth = column.getWidth();
widthDiff = newWidth - column.modules.vdomHoz.width;
if(widthDiff){
column.modules.vdomHoz.rightPos += widthDiff;
column.modules.vdomHoz.width = newWidth;
this.colPositionAdjust(this.columns.indexOf(column) + 1, this.columns.length, widthDiff);
}
column.modules.vdomHoz.fitDataCheck = false;
}
return widthDiff;
}
initializeRow(row){
if(row.type !== "group"){
row.modules.vdomHoz = {
leftCol:this.leftCol,
rightCol:this.rightCol,
};
if(this.table.modules.frozenColumns){
this.table.modules.frozenColumns.leftColumns.forEach((column) => {
this.appendCell(row, column);
});
}
for(let i = this.leftCol; i <= this.rightCol; i++){
this.appendCell(row, this.columns[i]);
}
if(this.table.modules.frozenColumns){
this.table.modules.frozenColumns.rightColumns.forEach((column) => {
this.appendCell(row, column);
});
}
}
}
appendCell(row, column){
if(column && column.visible){
let cell = row.getCell(column);
row.getElement().appendChild(cell.getElement());
cell.cellRendered();
}
}
reinitializeRow(row, force){
if(row.type !== "group"){
if(force || !row.modules.vdomHoz || row.modules.vdomHoz.leftCol !== this.leftCol || row.modules.vdomHoz.rightCol !== this.rightCol){
var rowEl = row.getElement();
while(rowEl.firstChild) rowEl.removeChild(rowEl.firstChild);
this.initializeRow(row);
}
}
}
}
class ColumnManager extends CoreFeature {
constructor (table){
super(table);
this.blockHozScrollEvent = false;
this.headersElement = null;
this.contentsElement = null;
this.element = null ; //containing element
this.columns = []; // column definition object
this.columnsByIndex = []; //columns by index
this.columnsByField = {}; //columns by field
this.scrollLeft = 0;
this.optionsList = new OptionsList(this.table, "column definition", defaultColumnOptions);
this.redrawBlock = false; //prevent redraws to allow multiple data manipulations before continuing
this.redrawBlockUpdate = null; //store latest redraw update only status
this.renderer = null;
}
////////////// Setup Functions /////////////////
initialize(){
this.initializeRenderer();
this.headersElement = this.createHeadersElement();
this.contentsElement = this.createHeaderContentsElement();
this.element = this.createHeaderElement();
this.contentsElement.insertBefore(this.headersElement, this.contentsElement.firstChild);
this.element.insertBefore(this.contentsElement, this.element.firstChild);
this.subscribe("scroll-horizontal", this.scrollHorizontal.bind(this));
this.subscribe("scrollbar-vertical", this.padVerticalScrollbar.bind(this));
}
padVerticalScrollbar(width){
if(this.table.rtl){
this.headersElement.style.marginLeft = width + "px";
}else {
this.headersElement.style.marginRight = width + "px";
}
}
initializeRenderer(){
var renderClass;
var renderers = {
"virtual": VirtualDomHorizontal,
"basic": BasicHorizontal,
};
if(typeof this.table.options.renderHorizontal === "string"){
renderClass = renderers[this.table.options.renderHorizontal];
}else {
renderClass = this.table.options.renderHorizontal;
}
if(renderClass){
this.renderer = new renderClass(this.table, this.element, this.tableElement);
this.renderer.initialize();
}else {
console.error("Unable to find matching renderer:", this.table.options.renderHorizontal);
}
}
createHeadersElement (){
var el = document.createElement("div");
el.classList.add("tabulator-headers");
el.setAttribute("role", "row");
return el;
}
createHeaderContentsElement (){
var el = document.createElement("div");
el.classList.add("tabulator-header-contents");
el.setAttribute("role", "rowgroup");
return el;
}
createHeaderElement (){
var el = document.createElement("div");
el.classList.add("tabulator-header");
el.setAttribute("role", "rowgroup");
if(!this.table.options.headerVisible){
el.classList.add("tabulator-header-hidden");
}
return el;
}
//return containing element
getElement(){
return this.element;
}
//return containing contents element
getContentsElement(){
return this.contentsElement;
}
//return header containing element
getHeadersElement(){
return this.headersElement;
}
//scroll horizontally to match table body
scrollHorizontal(left){
this.contentsElement.scrollLeft = left;
this.scrollLeft = left;
this.renderer.scrollColumns(left);
}
///////////// Column Setup Functions /////////////
generateColumnsFromRowData(data){
var cols = [],
definitions = this.table.options.autoColumnsDefinitions,
row, sorter;
if(data && data.length){
row = data[0];
for(var key in row){
let col = {
field:key,
title:key,
};
let value = row[key];
switch(typeof value){
case "undefined":
sorter = "string";
break;
case "boolean":
sorter = "boolean";
break;
case "object":
if(Array.isArray(value)){
sorter = "array";
}else {
sorter = "string";
}
break;
default:
if(!isNaN(value) && value !== ""){
sorter = "number";
}else {
if(value.match(/((^[0-9]+[a-z]+)|(^[a-z]+[0-9]+))+$/i)){
sorter = "alphanum";
}else {
sorter = "string";
}
}
break;
}
col.sorter = sorter;
cols.push(col);
}
if(definitions){
switch(typeof definitions){
case "function":
this.table.options.columns = definitions.call(this.table, cols);
break;
case "object":
if(Array.isArray(definitions)){
cols.forEach((col) => {
var match = definitions.find((def) => {
return def.field === col.field;
});
if(match){
Object.assign(col, match);
}
});
}else {
cols.forEach((col) => {
if(definitions[col.field]){
Object.assign(col, definitions[col.field]);
}
});
}
this.table.options.columns = cols;
break;
}
}else {
this.table.options.columns = cols;
}
this.setColumns(this.table.options.columns);
}
}
setColumns(cols, row){
while(this.headersElement.firstChild) this.headersElement.removeChild(this.headersElement.firstChild);
this.columns = [];
this.columnsByIndex = [];
this.columnsByField = {};
this.dispatch("columns-loading");
cols.forEach((def, i) => {
this._addColumn(def);
});
this._reIndexColumns();
this.dispatch("columns-loaded");
this.rerenderColumns(false, true);
this.redraw(true);
}
_addColumn(definition, before, nextToColumn){
var column = new Column(definition, this),
colEl = column.getElement(),
index = nextToColumn ? this.findColumnIndex(nextToColumn) : nextToColumn;
if(nextToColumn && index > -1){
var topColumn = nextToColumn.getTopColumn();
var parentIndex = this.columns.indexOf(topColumn);
var nextEl = topColumn.getElement();
if(before){
this.columns.splice(parentIndex, 0, column);
nextEl.parentNode.insertBefore(colEl, nextEl);
}else {
this.columns.splice(parentIndex + 1, 0, column);
nextEl.parentNode.insertBefore(colEl, nextEl.nextSibling);
}
}else {
if(before){
this.columns.unshift(column);
this.headersElement.insertBefore(column.getElement(), this.headersElement.firstChild);
}else {
this.columns.push(column);
this.headersElement.appendChild(column.getElement());
}
}
column.columnRendered();
return column;
}
registerColumnField(col){
if(col.definition.field){
this.columnsByField[col.definition.field] = col;
}
}
registerColumnPosition(col){
this.columnsByIndex.push(col);
}
_reIndexColumns(){
this.columnsByIndex = [];
this.columns.forEach(function(column){
column.reRegisterPosition();
});
}
//ensure column headers take up the correct amount of space in column groups
verticalAlignHeaders(){
var minHeight = 0;
if(!this.redrawBlock){
this.headersElement.style.height="";
this.columns.forEach((column) => {
column.clearVerticalAlign();
});
this.columns.forEach((column) => {
var height = column.getHeight();
if(height > minHeight){
minHeight = height;
}
});
this.headersElement.style.height = minHeight + "px";
this.columns.forEach((column) => {
column.verticalAlign(this.table.options.columnHeaderVertAlign, minHeight);
});
this.table.rowManager.adjustTableSize();
}
}
//////////////// Column Details /////////////////
findColumn(subject){
var columns;
if(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(typeof HTMLElement !== "undefined" && subject instanceof HTMLElement){
columns = [];
this.columns.forEach((column) => {
columns.push(column);
columns = columns.concat(column.getColumns(true));
});
//subject is a HTML element of the column header
let match = columns.find((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;
}
getColumnByField(field){
return this.columnsByField[field];
}
getColumnsByFieldRoot(root){
var matches = [];
Object.keys(this.columnsByField).forEach((field) => {
var fieldRoot = field.split(".")[0];
if(fieldRoot === root){
matches.push(this.columnsByField[field]);
}
});
return matches;
}
getColumnByIndex(index){
return this.columnsByIndex[index];
}
getFirstVisibleColumn(){
var index = this.columnsByIndex.findIndex((col) => {
return col.visible;
});
return index > -1 ? this.columnsByIndex[index] : false;
}
getColumns(){
return this.columns;
}
findColumnIndex(column){
return this.columnsByIndex.findIndex((col) => {
return column === col;
});
}
//return all columns that are not groups
getRealColumns(){
return this.columnsByIndex;
}
//traverse across columns and call action
traverse(callback){
this.columnsByIndex.forEach((column,i) =>{
callback(column, i);
});
}
//get definitions of actual columns
getDefinitions(active){
var output = [];
this.columnsByIndex.forEach((column) => {
if(!active || (active && column.visible)){
output.push(column.getDefinition());
}
});
return output;
}
//get full nested definition tree
getDefinitionTree(){
var output = [];
this.columns.forEach((column) => {
output.push(column.getDefinition(true));
});
return output;
}
getComponents(structured){
var output = [],
columns = structured ? this.columns : this.columnsByIndex;
columns.forEach((column) => {
output.push(column.getComponent());
});
return output;
}
getWidth(){
var width = 0;
this.columnsByIndex.forEach((column) => {
if(column.visible){
width += column.getWidth();
}
});
return width;
}
moveColumn(from, to, after){
to.element.parentNode.insertBefore(from.element, to.element);
if(after){
to.element.parentNode.insertBefore(to.element, from.element);
}
this.moveColumnActual(from, to, after);
this.verticalAlignHeaders();
this.table.rowManager.reinitialize();
}
moveColumnActual(from, to, after){
if(from.parent.isGroup){
this._moveColumnInArray(from.parent.columns, from, to, after);
}else {
this._moveColumnInArray(this.columns, from, to, after);
}
this._moveColumnInArray(this.columnsByIndex, from, to, after, true);
this.rerenderColumns(true);
this.dispatch("column-moved", from, to, after);
if(this.subscribedExternal("columnMoved")){
this.dispatchExternal("columnMoved", from.getComponent(), this.table.columnManager.getComponents());
}
}
_moveColumnInArray(columns, from, to, after, updateRows){
var fromIndex = columns.indexOf(from),
toIndex, rows = [];
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){
rows = this.chain("column-moving-rows", [from, to, after], null, []) || [];
rows = rows.concat(this.table.rowManager.rows);
rows.forEach(function(row){
if(row.cells.length){
var cell = row.cells.splice(fromIndex, 1)[0];
row.cells.splice(toIndex, 0, cell);
}
});
}
}
}
scrollToColumn(column, position, ifVisible){
var left = 0,
offset = column.getLeftOffset(),
adjust = 0,
colEl = column.getElement();
return new Promise((resolve, reject) => {
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.clientWidth / 2;
break;
case "right":
adjust = colEl.clientWidth - this.headersElement.clientWidth;
break;
}
//check column visibility
if(!ifVisible){
if(offset > 0 && offset + colEl.offsetWidth < this.element.clientWidth){
return false;
}
}
//calculate scroll position
left = offset + adjust;
left = Math.max(Math.min(left, this.table.rowManager.element.scrollWidth - this.table.rowManager.element.clientWidth),0);
this.table.rowManager.scrollHorizontal(left);
this.scrollHorizontal(left);
resolve();
}else {
console.warn("Scroll Error - Column not visible");
reject("Scroll Error - Column not visible");
}
});
}
//////////////// Cell Management /////////////////
generateCells(row){
var cells = [];
this.columnsByIndex.forEach((column) => {
cells.push(column.generateCell(row));
});
return cells;
}
//////////////// Column Management /////////////////
getFlexBaseWidth(){
var totalWidth = this.table.element.clientWidth, //table element width
fixedWidth = 0;
//adjust for vertical scrollbar if present
if(this.table.rowManager.element.scrollHeight > this.table.rowManager.element.clientHeight){
totalWidth -= this.table.rowManager.element.offsetWidth - this.table.rowManager.element.clientWidth;
}
this.columnsByIndex.forEach(function(column){
var width, minWidth, colWidth;
if(column.visible){
width = column.definition.width || 0;
minWidth = 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;
}
addColumn(definition, before, nextToColumn){
return new Promise((resolve, reject) => {
var column = this._addColumn(definition, before, nextToColumn);
this._reIndexColumns();
this.dispatch("column-add", definition, before, nextToColumn);
if(this.layoutMode() != "fitColumns"){
column.reinitializeWidth();
}
this.redraw(true);
this.table.rowManager.reinitialize();
this.rerenderColumns();
resolve(column);
});
}
//remove column from system
deregisterColumn(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);
}
this.verticalAlignHeaders();
this.redraw();
}
rerenderColumns(update, silent){
if(!this.redrawBlock){
this.renderer.rerenderColumns(update, silent);
}else {
if(update === false || (update === true && this.redrawBlockUpdate === null)){
this.redrawBlockUpdate = update;
}
}
}
blockRedraw(){
this.redrawBlock = true;
this.redrawBlockUpdate = null;
}
restoreRedraw(){
this.redrawBlock = false;
this.verticalAlignHeaders();
this.renderer.rerenderColumns(this.redrawBlockUpdate);
}
//redraw columns
redraw(force){
if(Helpers.elVisible(this.element)){
this.verticalAlignHeaders();
}
if(force){
this.table.rowManager.resetScroll();
this.table.rowManager.reinitialize();
}
if(!this.confirm("table-redrawing", force)){
this.layoutRefresh(force);
}
this.dispatch("table-redraw", force);
this.table.footerManager.redraw();
}
}
class BasicVertical extends Renderer{
constructor(table){
super(table);
this.verticalFillMode = "fill";
this.scrollTop = 0;
this.scrollLeft = 0;
this.scrollTop = 0;
this.scrollLeft = 0;
}
clearRows(){
var element = this.tableElement;
// element.children.detach();
while(element.firstChild) element.removeChild(element.firstChild);
element.scrollTop = 0;
element.scrollLeft = 0;
element.style.minWidth = "";
element.style.minHeight = "";
element.style.display = "";
element.style.visibility = "";
}
renderRows(){
var element = this.tableElement,
onlyGroupHeaders = true;
this.rows().forEach((row, index) => {
this.styleRow(row, index);
element.appendChild(row.getElement());
row.initialize(true);
if(row.type !== "group"){
onlyGroupHeaders = false;
}
});
if(onlyGroupHeaders){
element.style.minWidth = this.table.columnManager.getWidth() + "px";
}else {
element.style.minWidth = "";
}
}
rerenderRows(callback){
this.clearRows();
if(callback){
callback();
}
this.renderRows();
}
scrollToRowNearestTop(row){
var rowTop = Helpers.elOffset(row.getElement()).top;
return !(Math.abs(this.elementVertical.scrollTop - rowTop) > Math.abs(this.elementVertical.scrollTop + this.elementVertical.clientHeight - rowTop));
}
scrollToRow(row){
var rowEl = row.getElement();
this.elementVertical.scrollTop = Helpers.elOffset(rowEl).top - Helpers.elOffset(this.elementVertical).top + this.elementVertical.scrollTop;
}
visibleRows(includingBuffer){
return this.rows();
}
}
class VirtualDomVertical extends Renderer{
constructor(table){
super(table);
this.verticalFillMode = "fill";
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 position 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
}
//////////////////////////////////////
///////// Public Functions ///////////
//////////////////////////////////////
clearRows(){
var element = this.tableElement;
// element.children.detach();
while(element.firstChild) element.removeChild(element.firstChild);
element.style.paddingTop = "";
element.style.paddingBottom = "";
// element.style.minWidth = "";
element.style.minHeight = "";
element.style.display = "";
element.style.visibility = "";
this.elementVertical.scrollTop = 0;
this.elementVertical.scrollLeft = 0;
this.scrollTop = 0;
this.scrollLeft = 0;
this.vDomTop = 0;
this.vDomBottom = 0;
this.vDomTopPad = 0;
this.vDomBottomPad = 0;
this.vDomScrollPosTop = 0;
this.vDomScrollPosBottom = 0;
}
renderRows(){
this._virtualRenderFill();
}
rerenderRows(callback){
var scrollTop = this.elementVertical.scrollTop;
var topRow = false;
var topOffset = false;
var left = this.table.rowManager.scrollLeft;
var rows = this.rows();
for(var i = this.vDomTop; i <= this.vDomBottom; i++){
if(rows[i]){
var diff = scrollTop - rows[i].getElement().offsetTop;
if(topOffset === false || Math.abs(diff) < topOffset){
topOffset = diff;
topRow = i;
}else {
break;
}
}
}
rows.forEach((row) => {
row.deinitializeHeight();
});
if(callback){
callback();
}
if(this.rows().length){
this._virtualRenderFill((topRow === false ? this.rows.length - 1 : topRow), true, topOffset || 0);
}else {
this.clear();
this.table.rowManager.tableEmpty();
}
this.scrollColumns(left);
}
scrollColumns(left){
this.table.rowManager.scrollHorizontal(left);
}
scrollRows(top, dir){
var topDiff = top - this.vDomScrollPosTop;
var bottomDiff = top - this.vDomScrollPosBottom;
var margin = this.vDomWindowBuffer * 2;
var rows = this.rows();
this.scrollTop = top;
if(-topDiff > margin || bottomDiff > margin){
//if big scroll redraw table;
var left = this.table.rowManager.scrollLeft;
this._virtualRenderFill(Math.floor((this.elementVertical.scrollTop / this.elementVertical.scrollHeight) * rows.length));
this.scrollColumns(left);
}else {
if(dir){
//scrolling up
if(topDiff < 0){
this._addTopRow(rows, -topDiff);
}
if(bottomDiff < 0){
//hide bottom row if needed
if(this.vDomScrollHeight - this.scrollTop > this.vDomWindowBuffer){
this._removeBottomRow(rows, -bottomDiff);
}else {
this.vDomScrollPosBottom = this.scrollTop;
}
}
}else {
if(bottomDiff >= 0){
this._addBottomRow(rows, bottomDiff);
}
//scrolling down
if(topDiff >= 0){
//hide top row if needed
if(this.scrollTop > this.vDomWindowBuffer){
this._removeTopRow(rows, topDiff);
}else {
this.vDomScrollPosTop = this.scrollTop;
}
}
}
}
}
resize(){
this.vDomWindowBuffer = this.table.options.renderVerticalBuffer || this.elementVertical.clientHeight;
}
scrollToRowNearestTop(row){
var rowIndex = this.rows().indexOf(row);
return !(Math.abs(this.vDomTop - rowIndex) > Math.abs(this.vDomBottom - rowIndex));
}
scrollToRow(row){
var index = this.rows().indexOf(row);
if(index > -1){
this._virtualRenderFill(index, true);
}
}
visibleRows(includingBuffer){
var topEdge = this.elementVertical.scrollTop,
bottomEdge = this.elementVertical.clientHeight + topEdge,
topFound = false,
topRow = 0,
bottomRow = 0,
rows = this.rows();
if(includingBuffer){
topRow = this.vDomTop;
bottomRow = this.vDomBottom;
}else {
for(var i = this.vDomTop; i <= this.vDomBottom; i++){
if(rows[i]){
if(!topFound){
if((topEdge - rows[i].getElement().offsetTop) >= 0){
topRow = i;
}else {
topFound = true;
if(bottomEdge - rows[i].getElement().offsetTop >= 0){
bottomRow = i;
}else {
break;
}
}
}else {
if(bottomEdge - rows[i].getElement().offsetTop >= 0){
bottomRow = i;
}else {
break;
}
}
}
}
}
return rows.slice(topRow, bottomRow + 1);
}
//////////////////////////////////////
//////// Internal Rendering //////////
//////////////////////////////////////
//full virtual render
_virtualRenderFill(position, forceMove, offset){
var element = this.tableElement,
holder = this.elementVertical,
topPad = 0,
rowsHeight = 0,
heightOccupied = 0,
topPadHeight = 0,
i = 0,
rows = this.rows(),
rowsCount = rows.length,
containerHeight = this.elementVertical.clientHeight;
position = position || 0;
offset = offset || 0;
if(!position){
this.clear();
}else {
while(element.firstChild) element.removeChild(element.firstChild);
//check if position is too close to bottom of table
heightOccupied = (rowsCount - position + 1) * this.vDomRowHeight;
if(heightOccupied < containerHeight){
position -= Math.ceil((containerHeight - heightOccupied) / this.vDomRowHeight);
if(position < 0){
position = 0;
}
}
//calculate initial pad
topPad = Math.min(Math.max(Math.floor(this.vDomWindowBuffer / this.vDomRowHeight), this.vDomWindowMinMarginRows), position);
position -= topPad;
}
if(rowsCount && Helpers.elVisible(this.elementVertical)){
this.vDomTop = position;
this.vDomBottom = position -1;
while ((rowsHeight <= containerHeight + this.vDomWindowBuffer || i < this.vDomWindowMinTotalRows) && this.vDomBottom < rowsCount -1){
var index = this.vDomBottom + 1,
row = rows[index],
rowHeight = 0;
this.styleRow(row, index);
element.appendChild(row.getElement());
row.initialize();
if(!row.heightInitialized){
row.normalizeHeight(true);
}
rowHeight = row.getHeight();
if(i < topPad){
topPadHeight += rowHeight;
}else {
rowsHeight += rowHeight;
}
if(rowHeight > this.vDomWindowBuffer){
this.vDomWindowBuffer = rowHeight * 2;
}
this.vDomBottom ++;
i++;
}
if(!position){
this.vDomTopPad = 0;
//adjust row height to match average of rendered elements
this.vDomRowHeight = Math.floor((rowsHeight + topPadHeight) / i);
this.vDomBottomPad = this.vDomRowHeight * (rowsCount - this.vDomBottom -1);
this.vDomScrollHeight = topPadHeight + rowsHeight + this.vDomBottomPad - containerHeight;
}else {
this.vDomTopPad = !forceMove ? this.scrollTop - topPadHeight : (this.vDomRowHeight * this.vDomTop) + offset;
this.vDomBottomPad = this.vDomBottom == rowsCount-1 ? 0 : Math.max(this.vDomScrollHeight - this.vDomTopPad - rowsHeight - topPadHeight, 0);
}
element.style.paddingTop = this.vDomTopPad + "px";
element.style.paddingBottom = this.vDomBottomPad + "px";
if(forceMove){
this.scrollTop = this.vDomTopPad + (topPadHeight) + offset - (this.elementVertical.scrollWidth > this.elementVertical.clientWidth ? this.elementVertical.offsetHeight - containerHeight : 0);
}
this.scrollTop = Math.min(this.scrollTop, this.elementVertical.scrollHeight - containerHeight);
//adjust for horizontal scrollbar if present (and not at top of table)
if(this.elementVertical.scrollWidth > this.elementVertical.clientWidth && forceMove){
this.scrollTop += this.elementVertical.offsetHeight - containerHeight;
}
this.vDomScrollPosTop = this.scrollTop;
this.vDomScrollPosBottom = this.scrollTop;
holder.scrollTop = this.scrollTop;
this.dispatch("render-virtual-fill");
}
}
_addTopRow(rows, fillableSpace){
var table = this.tableElement,
addedRows = [],
paddingAdjust = 0,
index = this.vDomTop -1,
i = 0,
working = true;
while(working){
if(this.vDomTop){
let row = rows[index],
rowHeight, initialized;
if(row && i < this.vDomMaxRenderChain){
rowHeight = row.getHeight() || this.vDomRowHeight;
initialized = row.initialized;
if(fillableSpace >= rowHeight){
this.styleRow(row, index);
table.insertBefore(row.getElement(), table.firstChild);
if(!row.initialized || !row.heightInitialized){
addedRows.push(row);
}
row.initialize();
if(!initialized){
rowHeight = row.getElement().offsetHeight;
if(rowHeight > this.vDomWindowBuffer){
this.vDomWindowBuffer = rowHeight * 2;
}
}
fillableSpace -= rowHeight;
paddingAdjust += rowHeight;
this.vDomTop--;
index--;
i++;
}else {
working = false;
}
}else {
working = false;
}
}else {
working = false;
}
}
for (let row of addedRows){
row.clearCellHeight();
}
this._quickNormalizeRowHeight(addedRows);
if(paddingAdjust){
this.vDomTopPad -= paddingAdjust;
if(this.vDomTopPad < 0){
this.vDomTopPad = index * this.vDomRowHeight;
}
if(index < 1){
this.vDomTopPad = 0;
}
table.style.paddingTop = this.vDomTopPad + "px";
this.vDomScrollPosTop -= paddingAdjust;
}
}
_removeTopRow(rows, fillableSpace){
var removableRows = [],
paddingAdjust = 0,
i = 0,
working = true;
while(working){
let row = rows[this.vDomTop],
rowHeight;
if(row && i < this.vDomMaxRenderChain){
rowHeight = row.getHeight() || this.vDomRowHeight;
if(fillableSpace >= rowHeight){
this.vDomTop++;
fillableSpace -= rowHeight;
paddingAdjust += rowHeight;
removableRows.push(row);
i++;
}else {
working = false;
}
}else {
working = false;
}
}
for (let row of removableRows){
let rowEl = row.getElement();
if(rowEl.parentNode){
rowEl.parentNode.removeChild(rowEl);
}
}
if(paddingAdjust){
this.vDomTopPad += paddingAdjust;
this.tableElement.style.paddingTop = this.vDomTopPad + "px";
this.vDomScrollPosTop += this.vDomTop ? paddingAdjust : paddingAdjust + this.vDomWindowBuffer;
}
}
_addBottomRow(rows, fillableSpace){
var table = this.tableElement,
addedRows = [],
paddingAdjust = 0,
index = this.vDomBottom + 1,
i = 0,
working = true;
while(working){
let row = rows[index],
rowHeight, initialized;
if(row && i < this.vDomMaxRenderChain){
rowHeight = row.getHeight() || this.vDomRowHeight;
initialized = row.initialized;
if(fillableSpace >= rowHeight){
this.styleRow(row, index);
table.appendChild(row.getElement());
if(!row.initialized || !row.heightInitialized){
addedRows.push(row);
}
row.initialize();
if(!initialized){
rowHeight = row.getElement().offsetHeight;
if(rowHeight > this.vDomWindowBuffer){
this.vDomWindowBuffer = rowHeight * 2;
}
}
fillableSpace -= rowHeight;
paddingAdjust += rowHeight;
this.vDomBottom++;
index++;
i++;
}else {
working = false;
}
}else {
working = false;
}
}
for (let row of addedRows){
row.clearCellHeight();
}
this._quickNormalizeRowHeight(addedRows);
if(paddingAdjust){
this.vDomBottomPad -= paddingAdjust;
if(this.vDomBottomPad < 0 || index == rows.length -1){
this.vDomBottomPad = 0;
}
table.style.paddingBottom = this.vDomBottomPad + "px";
this.vDomScrollPosBottom += paddingAdjust;
}
}
_removeBottomRow(rows, fillableSpace){
var removableRows = [],
paddingAdjust = 0,
i = 0,
working = true;
while(working){
let row = rows[this.vDomBottom],
rowHeight;
if(row && i < this.vDomMaxRenderChain){
rowHeight = row.getHeight() || this.vDomRowHeight;
if(fillableSpace >= rowHeight){
this.vDomBottom --;
fillableSpace -= rowHeight;
paddingAdjust += rowHeight;
removableRows.push(row);
i++;
}else {
working = false;
}
}else {
working = false;
}
}
for (let row of removableRows){
let rowEl = row.getElement();
if(rowEl.parentNode){
rowEl.parentNode.removeChild(rowEl);
}
}
if(paddingAdjust){
this.vDomBottomPad += paddingAdjust;
if(this.vDomBottomPad < 0){
this.vDomBottomPad = 0;
}
this.tableElement.style.paddingBottom = this.vDomBottomPad + "px";
this.vDomScrollPosBottom -= paddingAdjust;
}
}
_quickNormalizeRowHeight(rows){
for(let row of rows){
row.calcHeight();
}
for(let row of rows){
row.setCellHeight();
}
}
}
class RowManager extends CoreFeature{
constructor(table){
super(table);
this.element = this.createHolderElement(); //containing element
this.tableElement = this.createTableElement(); //table element
this.heightFixer = this.createTableElement(); //table element
this.placeholder = null; //placeholder element
this.placeholderContents = null; //placeholder element
this.firstRender = false; //handle first render
this.renderMode = "virtual"; //current rendering mode
this.fixedHeight = false; //current rendering mode
this.rows = []; //hold row data objects
this.activeRowsPipeline = []; //hold calculation of active rows
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.redrawBlock = false; //prevent redraws to allow multiple data manipulations before continuing
this.redrawBlockRestoreConfig = false; //store latest redraw function calls for when redraw is needed
this.redrawBlockRenderInPosition = false; //store latest redraw function calls for when redraw is needed
this.dataPipeline = []; //hold data pipeline tasks
this.displayPipeline = []; //hold data display pipeline tasks
this.scrollbarWidth = 0;
this.renderer = null;
}
//////////////// Setup Functions /////////////////
createHolderElement (){
var el = document.createElement("div");
el.classList.add("tabulator-tableholder");
el.setAttribute("tabindex", 0);
// el.setAttribute("role", "rowgroup");
return el;
}
createTableElement (){
var el = document.createElement("div");
el.classList.add("tabulator-table");
el.setAttribute("role", "rowgroup");
return el;
}
initializePlaceholder(){
var placeholder = this.table.options.placeholder;
//configure placeholder element
if(placeholder){
let el = document.createElement("div");
el.classList.add("tabulator-placeholder");
if(typeof placeholder == "string"){
let contents = document.createElement("div");
contents.classList.add("tabulator-placeholder-contents");
contents.innerHTML = placeholder;
el.appendChild(contents);
this.placeholderContents = contents;
}else if(typeof HTMLElement !== "undefined" && placeholder instanceof HTMLElement){
el.appendChild(placeholder);
this.placeholderContents = placeholder;
}else {
console.warn("Invalid placeholder provided, must be string or HTML Element", placeholder);
this.el = null;
}
this.placeholder = el;
}
}
//return containing element
getElement(){
return this.element;
}
//return table element
getTableElement(){
return this.tableElement;
}
initialize(){
this.initializePlaceholder();
this.initializeRenderer();
//initialize manager
this.element.appendChild(this.tableElement);
this.firstRender = true;
//scroll header along with table body
this.element.addEventListener("scroll", () => {
var left = this.element.scrollLeft,
leftDir = this.scrollLeft > left,
top = this.element.scrollTop,
topDir = this.scrollTop > top;
//handle horizontal scrolling
if(this.scrollLeft != left){
this.scrollLeft = left;
this.dispatch("scroll-horizontal", left, leftDir);
this.dispatchExternal("scrollHorizontal", left, leftDir);
this._positionPlaceholder();
}
//handle vertical scrolling
if(this.scrollTop != top){
this.scrollTop = top;
this.renderer.scrollRows(top, topDir);
this.dispatch("scroll-vertical", top, topDir);
this.dispatchExternal("scrollVertical", top, topDir);
}
});
}
////////////////// Row Manipulation //////////////////
findRow(subject){
if(typeof subject == "object"){
if(subject instanceof Row){
//subject is row element
return subject;
}else if(subject instanceof RowComponent){
//subject is public row component
return subject._getSelf() || false;
}else if(typeof HTMLElement !== "undefined" && subject instanceof HTMLElement){
//subject is a HTML element of the row
let match = this.rows.find((row) => {
return row.getElement() === subject;
});
return match || false;
}else if(subject === null){
return false;
}
}else if(typeof subject == "undefined"){
return false;
}else {
//subject should be treated as the index of the row
let match = this.rows.find((row) => {
return row.data[this.table.options.index] == subject;
});
return match || false;
}
//catch all for any other type of input
return false;
}
getRowFromDataObject(data){
var match = this.rows.find((row) => {
return row.data === data;
});
return match || false;
}
getRowFromPosition(position){
return this.getDisplayRows().find((row) => {
return row.getPosition() === position && row.isDisplayed();
});
}
scrollToRow(row, position, ifVisible){
return this.renderer.scrollToRowPosition(row, position, ifVisible);
}
////////////////// Data Handling //////////////////
setData(data, renderInPosition, columnsChanged){
return new Promise((resolve, reject)=>{
if(renderInPosition && this.getDisplayRows().length){
if(this.table.options.pagination){
this._setDataActual(data, true);
}else {
this.reRenderInPosition(() => {
this._setDataActual(data);
});
}
}else {
if(this.table.options.autoColumns && columnsChanged && this.table.initialized){
this.table.columnManager.generateColumnsFromRowData(data);
}
this.resetScroll();
this._setDataActual(data);
}
resolve();
});
}
_setDataActual(data, renderInPosition){
this.dispatchExternal("dataProcessing", data);
this._wipeElements();
if(Array.isArray(data)){
this.dispatch("data-processing", data);
data.forEach((def, i) => {
if(def && typeof def === "object"){
var row = new Row(def, this);
this.rows.push(row);
}else {
console.warn("Data Loading Warning - Invalid row data detected and ignored, expecting object but received:", def);
}
});
this.refreshActiveData(false, false, renderInPosition);
this.dispatch("data-processed", data);
this.dispatchExternal("dataProcessed", data);
}else {
console.error("Data Loading Error - Unable to process data due to invalid data type \nExpecting: array \nReceived: ", typeof data, "\nData: ", data);
}
}
_wipeElements(){
this.dispatch("rows-wipe");
this.destroy();
this.adjustTableSize();
this.dispatch("rows-wiped");
}
destroy(){
this.rows.forEach((row) => {
row.wipe();
});
this.rows = [];
this.activeRows = [];
this.activeRowsPipeline = [];
this.activeRowsCount = 0;
this.displayRows = [];
this.displayRowsCount = 0;
}
deleteRow(row, blockRedraw){
var allIndex = this.rows.indexOf(row),
activeIndex = this.activeRows.indexOf(row);
if(activeIndex > -1){
this.activeRows.splice(activeIndex, 1);
}
if(allIndex > -1){
this.rows.splice(allIndex, 1);
}
this.setActiveRows(this.activeRows);
this.displayRowIterator((rows) => {
var displayIndex = rows.indexOf(row);
if(displayIndex > -1){
rows.splice(displayIndex, 1);
}
});
if(!blockRedraw){
this.reRenderInPosition();
}
this.regenerateRowPositions();
this.dispatchExternal("rowDeleted", row.getComponent());
if(!this.displayRowsCount){
this.tableEmpty();
}
if(this.subscribedExternal("dataChanged")){
this.dispatchExternal("dataChanged", this.getData());
}
}
addRow(data, pos, index, blockRedraw){
var row = this.addRowActual(data, pos, index, blockRedraw);
return row;
}
//add multiple rows
addRows(data, pos, index, refreshDisplayOnly){
var rows = [];
return new Promise((resolve, reject) => {
pos = this.findAddRowPos(pos);
if(!Array.isArray(data)){
data = [data];
}
if((typeof index == "undefined" && pos) || (typeof index !== "undefined" && !pos)){
data.reverse();
}
data.forEach((item, i) => {
var row = this.addRow(item, pos, index, true);
rows.push(row);
this.dispatch("row-added", row, item, pos, index);
});
this.refreshActiveData(refreshDisplayOnly ? "displayPipeline" : false, false, true);
this.regenerateRowPositions();
if(rows.length){
this._clearPlaceholder();
}
resolve(rows);
});
}
findAddRowPos(pos){
if(typeof pos === "undefined"){
pos = this.table.options.addRowPos;
}
if(pos === "pos"){
pos = true;
}
if(pos === "bottom"){
pos = false;
}
return pos;
}
addRowActual(data, pos, index, blockRedraw){
var row = data instanceof Row ? data : new Row(data || {}, this),
top = this.findAddRowPos(pos),
allIndex = -1,
activeIndex, chainResult;
if(!index){
chainResult = this.chain("row-adding-position", [row, top], null, {index, top});
index = chainResult.index;
top = chainResult.top;
}
if(typeof index !== "undefined"){
index = this.findRow(index);
}
index = this.chain("row-adding-index", [row, index, top], null, index);
if(index){
allIndex = this.rows.indexOf(index);
}
if(index && allIndex > -1){
activeIndex = this.activeRows.indexOf(index);
this.displayRowIterator(function(rows){
var displayIndex = rows.indexOf(index);
if(displayIndex > -1){
rows.splice((top ? displayIndex : displayIndex + 1), 0, row);
}
});
if(activeIndex > -1){
this.activeRows.splice((top ? activeIndex : activeIndex + 1), 0, row);
}
this.rows.splice((top ? allIndex : allIndex + 1), 0, row);
}else {
if(top){
this.displayRowIterator(function(rows){
rows.unshift(row);
});
this.activeRows.unshift(row);
this.rows.unshift(row);
}else {
this.displayRowIterator(function(rows){
rows.push(row);
});
this.activeRows.push(row);
this.rows.push(row);
}
}
this.setActiveRows(this.activeRows);
this.dispatchExternal("rowAdded", row.getComponent());
if(this.subscribedExternal("dataChanged")){
this.dispatchExternal("dataChanged", this.table.rowManager.getData());
}
if(!blockRedraw){
this.reRenderInPosition();
}
return row;
}
moveRow(from, to, after){
this.dispatch("row-move", from, to, after);
this.moveRowActual(from, to, after);
this.regenerateRowPositions();
this.dispatch("row-moved", from, to, after);
this.dispatchExternal("rowMoved", from.getComponent());
}
moveRowActual(from, to, after){
this.moveRowInArray(this.rows, from, to, after);
this.moveRowInArray(this.activeRows, from, to, after);
this.displayRowIterator((rows) => {
this.moveRowInArray(rows, from, to, after);
});
this.dispatch("row-moving", from, to, after);
}
moveRowInArray(rows, from, to, after){
var fromIndex, toIndex, start, end;
if(from !== to){
fromIndex = rows.indexOf(from);
if (fromIndex > -1) {
rows.splice(fromIndex, 1);
toIndex = rows.indexOf(to);
if (toIndex > -1) {
if(after){
rows.splice(toIndex+1, 0, from);
}else {
rows.splice(toIndex, 0, from);
}
}else {
rows.splice(fromIndex, 0, from);
}
}
//restyle rows
if(rows === this.getDisplayRows()){
start = fromIndex < toIndex ? fromIndex : toIndex;
end = toIndex > fromIndex ? toIndex : fromIndex +1;
for(let i = start; i <= end; i++){
if(rows[i]){
this.styleRow(rows[i], i);
}
}
}
}
}
clearData(){
this.setData([]);
}
getRowIndex(row){
return this.findRowIndex(row, this.rows);
}
getDisplayRowIndex(row){
var index = this.getDisplayRows().indexOf(row);
return index > -1 ? index : false;
}
nextDisplayRow(row, rowOnly){
var index = this.getDisplayRowIndex(row),
nextRow = false;
if(index !== false && index < this.displayRowsCount -1){
nextRow = this.getDisplayRows()[index+1];
}
if(nextRow && (!(nextRow instanceof Row) || nextRow.type != "row")){
return this.nextDisplayRow(nextRow, rowOnly);
}
return nextRow;
}
prevDisplayRow(row, rowOnly){
var index = this.getDisplayRowIndex(row),
prevRow = false;
if(index){
prevRow = this.getDisplayRows()[index-1];
}
if(rowOnly && prevRow && (!(prevRow instanceof Row) || prevRow.type != "row")){
return this.prevDisplayRow(prevRow, rowOnly);
}
return prevRow;
}
findRowIndex(row, list){
var rowIndex;
row = this.findRow(row);
if(row){
rowIndex = list.indexOf(row);
if(rowIndex > -1){
return rowIndex;
}
}
return false;
}
getData(active, transform){
var output = [],
rows = this.getRows(active);
rows.forEach(function(row){
if(row.type == "row"){
output.push(row.getData(transform || "data"));
}
});
return output;
}
getComponents(active){
var output = [],
rows = this.getRows(active);
rows.forEach(function(row){
output.push(row.getComponent());
});
return output;
}
getDataCount(active){
var rows = this.getRows(active);
return rows.length;
}
scrollHorizontal(left){
this.scrollLeft = left;
this.element.scrollLeft = left;
this.dispatch("scroll-horizontal", left);
}
registerDataPipelineHandler(handler, priority){
if(typeof priority !== "undefined"){
this.dataPipeline.push({handler, priority});
this.dataPipeline.sort((a, b) => {
return a.priority - b.priority;
});
}else {
console.error("Data pipeline handlers must have a priority in order to be registered");
}
}
registerDisplayPipelineHandler(handler, priority){
if(typeof priority !== "undefined"){
this.displayPipeline.push({handler, priority});
this.displayPipeline.sort((a, b) => {
return a.priority - b.priority;
});
}else {
console.error("Display pipeline handlers must have a priority in order to be registered");
}
}
//set active data set
refreshActiveData(handler, skipStage, renderInPosition){
var table = this.table,
stage = "",
index = 0,
cascadeOrder = ["all", "dataPipeline", "display", "displayPipeline", "end"];
if(!this.table.destroyed){
if(typeof handler === "function"){
index = this.dataPipeline.findIndex((item) => {
return item.handler === handler;
});
if(index > -1){
stage = "dataPipeline";
if(skipStage){
if(index == this.dataPipeline.length - 1){
stage = "display";
}else {
index++;
}
}
}else {
index = this.displayPipeline.findIndex((item) => {
return item.handler === handler;
});
if(index > -1){
stage = "displayPipeline";
if(skipStage){
if(index == this.displayPipeline.length - 1){
stage = "end";
}else {
index++;
}
}
}else {
console.error("Unable to refresh data, invalid handler provided", handler);
return;
}
}
}else {
stage = handler || "all";
index = 0;
}
if(this.redrawBlock){
if(!this.redrawBlockRestoreConfig || (this.redrawBlockRestoreConfig && ((this.redrawBlockRestoreConfig.stage === stage && index < this.redrawBlockRestoreConfig.index) || (cascadeOrder.indexOf(stage) < cascadeOrder.indexOf(this.redrawBlockRestoreConfig.stage))))){
this.redrawBlockRestoreConfig = {
handler: handler,
skipStage: skipStage,
renderInPosition: renderInPosition,
stage:stage,
index:index,
};
}
return;
}else {
if(Helpers.elVisible(this.element)){
if(renderInPosition){
this.reRenderInPosition(this.refreshPipelines.bind(this, handler, stage, index, renderInPosition));
}else {
this.refreshPipelines(handler, stage, index, renderInPosition);
if(!handler){
this.table.columnManager.renderer.renderColumns();
}
this.renderTable();
if(table.options.layoutColumnsOnNewData){
this.table.columnManager.redraw(true);
}
}
}else {
this.refreshPipelines(handler, stage, index, renderInPosition);
}
this.dispatch("data-refreshed");
}
}
}
refreshPipelines(handler, stage, index, renderInPosition){
this.dispatch("data-refreshing");
if(!handler){
this.activeRowsPipeline[0] = this.rows.slice(0);
}
//cascade through data refresh stages
switch(stage){
case "all":
//handle case where all data needs refreshing
case "dataPipeline":
for(let i = index; i < this.dataPipeline.length; i++){
let result = this.dataPipeline[i].handler(this.activeRowsPipeline[i].slice(0));
this.activeRowsPipeline[i + 1] = result || this.activeRowsPipeline[i].slice(0);
}
this.setActiveRows(this.activeRowsPipeline[this.dataPipeline.length]);
case "display":
index = 0;
this.resetDisplayRows();
case "displayPipeline":
for(let i = index; i < this.displayPipeline.length; i++){
let result = this.displayPipeline[i].handler((i ? this.getDisplayRows(i - 1) : this.activeRows).slice(0), renderInPosition);
this.setDisplayRows(result || this.getDisplayRows(i - 1).slice(0), i);
}
case "end":
//case to handle scenario when trying to skip past end stage
this.regenerateRowPositions();
}
if(this.getDisplayRows().length){
this._clearPlaceholder();
}
}
//regenerate row positions
regenerateRowPositions(){
var rows = this.getDisplayRows();
var index = 1;
rows.forEach((row) => {
if (row.type === "row"){
row.setPosition(index);
index++;
}
});
}
setActiveRows(activeRows){
this.activeRows = this.activeRows = Object.assign([], activeRows);
this.activeRowsCount = this.activeRows.length;
}
//reset display rows array
resetDisplayRows(){
this.displayRows = [];
this.displayRows.push(this.activeRows.slice(0));
this.displayRowsCount = this.displayRows[0].length;
}
//set display row pipeline data
setDisplayRows(displayRows, index){
this.displayRows[index] = displayRows;
if(index == this.displayRows.length -1){
this.displayRowsCount = this.displayRows[this.displayRows.length -1].length;
}
}
getDisplayRows(index){
if(typeof index == "undefined"){
return this.displayRows.length ? this.displayRows[this.displayRows.length -1] : [];
}else {
return this.displayRows[index] || [];
}
}
getVisibleRows(chain, viewable){
var rows = Object.assign([], this.renderer.visibleRows(!viewable));
if(chain){
rows = this.chain("rows-visible", [viewable], rows, rows);
}
return rows;
}
//repeat action across display rows
displayRowIterator(callback){
this.activeRowsPipeline.forEach(callback);
this.displayRows.forEach(callback);
this.displayRowsCount = this.displayRows[this.displayRows.length -1].length;
}
//return only actual rows (not group headers etc)
getRows(type){
var rows = [];
switch(type){
case "active":
rows = this.activeRows;
break;
case "display":
rows = this.table.rowManager.getDisplayRows();
break;
case "visible":
rows = this.getVisibleRows(false, true);
break;
default:
rows = this.chain("rows-retrieve", type, null, this.rows) || this.rows;
}
return rows;
}
///////////////// Table Rendering /////////////////
//trigger rerender of table in current position
reRenderInPosition(callback){
if(this.redrawBlock){
if(callback){
callback();
}else {
this.redrawBlockRenderInPosition = true;
}
}else {
this.dispatchExternal("renderStarted");
this.renderer.rerenderRows(callback);
if(!this.fixedHeight){
this.adjustTableSize();
}
this.scrollBarCheck();
this.dispatchExternal("renderComplete");
}
}
scrollBarCheck(){
var scrollbarWidth = 0;
//adjust for vertical scrollbar moving table when present
if(this.element.scrollHeight > this.element.clientHeight){
scrollbarWidth = this.element.offsetWidth - this.element.clientWidth;
}
if(scrollbarWidth !== this.scrollbarWidth){
this.scrollbarWidth = scrollbarWidth;
this.dispatch("scrollbar-vertical", scrollbarWidth);
}
}
initializeRenderer(){
var renderClass;
var renderers = {
"virtual": VirtualDomVertical,
"basic": BasicVertical,
};
if(typeof this.table.options.renderVertical === "string"){
renderClass = renderers[this.table.options.renderVertical];
}else {
renderClass = this.table.options.renderVertical;
}
if(renderClass){
this.renderMode = this.table.options.renderVertical;
this.renderer = new renderClass(this.table, this.element, this.tableElement);
this.renderer.initialize();
if((this.table.element.clientHeight || this.table.options.height) && !(this.table.options.minHeight && this.table.options.maxHeight)){
this.fixedHeight = true;
}else {
this.fixedHeight = false;
}
}else {
console.error("Unable to find matching renderer:", this.table.options.renderVertical);
}
}
getRenderMode(){
return this.renderMode;
}
renderTable(){
this.dispatchExternal("renderStarted");
this.element.scrollTop = 0;
this._clearTable();
if(this.displayRowsCount){
this.renderer.renderRows();
if(this.firstRender){
this.firstRender = false;
if(!this.fixedHeight){
this.adjustTableSize();
}
this.layoutRefresh(true);
}
}else {
this.renderEmptyScroll();
}
if(!this.fixedHeight){
this.adjustTableSize();
}
this.dispatch("table-layout");
if(!this.displayRowsCount){
this._showPlaceholder();
}
this.scrollBarCheck();
this.dispatchExternal("renderComplete");
}
//show scrollbars on empty table div
renderEmptyScroll(){
if(this.placeholder){
this.tableElement.style.display = "none";
}else {
this.tableElement.style.minWidth = this.table.columnManager.getWidth() + "px";
// this.tableElement.style.minHeight = "1px";
// this.tableElement.style.visibility = "hidden";
}
}
_clearTable(){
this._clearPlaceholder();
this.scrollTop = 0;
this.scrollLeft = 0;
this.renderer.clearRows();
}
tableEmpty(){
this.renderEmptyScroll();
this._showPlaceholder();
}
_showPlaceholder(){
if(this.placeholder){
this.placeholder.setAttribute("tabulator-render-mode", this.renderMode);
this.getElement().appendChild(this.placeholder);
this._positionPlaceholder();
}
}
_clearPlaceholder(){
if(this.placeholder && this.placeholder.parentNode){
this.placeholder.parentNode.removeChild(this.placeholder);
}
// clear empty table placeholder min
this.tableElement.style.minWidth = "";
this.tableElement.style.display = "";
}
_positionPlaceholder(){
if(this.placeholder && this.placeholder.parentNode){
this.placeholder.style.width = this.table.columnManager.getWidth() + "px";
this.placeholderContents.style.width = this.table.rowManager.element.clientWidth + "px";
this.placeholderContents.style.marginLeft = this.scrollLeft + "px";
}
}
styleRow(row, index){
var rowEl = row.getElement();
if(index % 2){
rowEl.classList.add("tabulator-row-even");
rowEl.classList.remove("tabulator-row-odd");
}else {
rowEl.classList.add("tabulator-row-odd");
rowEl.classList.remove("tabulator-row-even");
}
}
//normalize height of active rows
normalizeHeight(){
this.activeRows.forEach(function(row){
row.normalizeHeight();
});
}
//adjust the height of the table holder to fit in the Tabulator element
adjustTableSize(){
var initialHeight = this.element.clientHeight, minHeight;
if(this.renderer.verticalFillMode === "fill"){
let otherHeight = Math.floor(this.table.columnManager.getElement().getBoundingClientRect().height + (this.table.footerManager && this.table.footerManager.active && !this.table.footerManager.external ? this.table.footerManager.getElement().getBoundingClientRect().height : 0));
if(this.fixedHeight){
minHeight = isNaN(this.table.options.minHeight) ? this.table.options.minHeight : this.table.options.minHeight + "px";
this.element.style.minHeight = minHeight || "calc(100% - " + otherHeight + "px)";
this.element.style.height = "calc(100% - " + otherHeight + "px)";
this.element.style.maxHeight = "calc(100% - " + otherHeight + "px)";
}else {
this.element.style.height = "";
this.element.style.height = (this.table.element.clientHeight - otherHeight) + "px";
this.element.scrollTop = this.scrollTop;
}
this.renderer.resize();
//check if the table has changed size when dealing with variable height tables
if(!this.fixedHeight && initialHeight != this.element.clientHeight){
if(this.subscribed("table-resize")){
this.dispatch("table-resize");
}else {
this.redraw();
}
}
this.scrollBarCheck();
}
this._positionPlaceholder();
}
//reinitialize all rows
reinitialize(){
this.rows.forEach(function(row){
row.reinitialize(true);
});
}
//prevent table from being redrawn
blockRedraw (){
this.redrawBlock = true;
this.redrawBlockRestoreConfig = false;
}
//restore table redrawing
restoreRedraw (){
this.redrawBlock = false;
if(this.redrawBlockRestoreConfig){
this.refreshActiveData(this.redrawBlockRestoreConfig.handler, this.redrawBlockRestoreConfig.skipStage, this.redrawBlockRestoreConfig.renderInPosition);
this.redrawBlockRestoreConfig = false;
}else {
if(this.redrawBlockRenderInPosition){
this.reRenderInPosition();
}
}
this.redrawBlockRenderInPosition = false;
}
//redraw table
redraw (force){
var left = this.scrollLeft;
this.adjustTableSize();
this.table.tableWidth = this.table.element.clientWidth;
if(!force){
this.reRenderInPosition();
this.scrollHorizontal(left);
}else {
this.renderTable();
}
}
resetScroll(){
this.element.scrollLeft = 0;
this.element.scrollTop = 0;
if(this.table.browser === "ie"){
var event = document.createEvent("Event");
event.initEvent("scroll", false, true);
this.element.dispatchEvent(event);
}else {
this.element.dispatchEvent(new Event('scroll'));
}
}
}
class FooterManager extends CoreFeature{
constructor(table){
super(table);
this.active = false;
this.element = this.createElement(); //containing element
this.containerElement = this.createContainerElement(); //containing element
this.external = false;
}
initialize(){
this.initializeElement();
}
createElement(){
var el = document.createElement("div");
el.classList.add("tabulator-footer");
return el;
}
createContainerElement(){
var el = document.createElement("div");
el.classList.add("tabulator-footer-contents");
this.element.appendChild(el);
return el;
}
initializeElement(){
if(this.table.options.footerElement){
switch(typeof this.table.options.footerElement){
case "string":
if(this.table.options.footerElement[0] === "<"){
this.containerElement.innerHTML = this.table.options.footerElement;
}else {
this.external = true;
this.containerElement = document.querySelector(this.table.options.footerElement);
}
break;
default:
this.element = this.table.options.footerElement;
break;
}
}
}
getElement(){
return this.element;
}
append(element){
this.activate();
this.containerElement.appendChild(element);
this.table.rowManager.adjustTableSize();
}
prepend(element){
this.activate();
this.element.insertBefore(element, this.element.firstChild);
this.table.rowManager.adjustTableSize();
}
remove(element){
element.parentNode.removeChild(element);
this.deactivate();
}
deactivate(force){
if(!this.element.firstChild || force){
if(!this.external){
this.element.parentNode.removeChild(this.element);
}
this.active = false;
}
}
activate(){
if(!this.active){
this.active = true;
if(!this.external){
this.table.element.appendChild(this.getElement());
this.table.element.style.display = '';
}
}
}
redraw(){
this.dispatch("footer-redraw");
}
}
class InteractionManager extends CoreFeature {
constructor (table){
super(table);
this.el = null;
this.abortClasses = ["tabulator-headers", "tabulator-table"];
this.previousTargets = {};
this.listeners = [
"click",
"dblclick",
"contextmenu",
"mouseenter",
"mouseleave",
"mouseover",
"mouseout",
"mousemove",
"mouseup",
"mousedown",
"touchstart",
"touchend",
];
this.componentMap = {
"tabulator-cell":"cell",
"tabulator-row":"row",
"tabulator-group":"group",
"tabulator-col":"column",
};
this.pseudoTrackers = {
"row":{
subscriber:null,
target:null,
},
"cell":{
subscriber:null,
target:null,
},
"group":{
subscriber:null,
target:null,
},
"column":{
subscriber:null,
target:null,
},
};
this.pseudoTracking = false;
}
initialize(){
this.el = this.table.element;
this.buildListenerMap();
this.bindSubscriptionWatchers();
}
buildListenerMap(){
var listenerMap = {};
this.listeners.forEach((listener) => {
listenerMap[listener] = {
handler:null,
components:[],
};
});
this.listeners = listenerMap;
}
bindPseudoEvents(){
Object.keys(this.pseudoTrackers).forEach((key) => {
this.pseudoTrackers[key].subscriber = this.pseudoMouseEnter.bind(this, key);
this.subscribe(key + "-mouseover", this.pseudoTrackers[key].subscriber);
});
this.pseudoTracking = true;
}
pseudoMouseEnter(key, e, target){
if(this.pseudoTrackers[key].target !== target){
if(this.pseudoTrackers[key].target){
this.dispatch(key + "-mouseleave", e, this.pseudoTrackers[key].target);
}
this.pseudoMouseLeave(key, e);
this.pseudoTrackers[key].target = target;
this.dispatch(key + "-mouseenter", e, target);
}
}
pseudoMouseLeave(key, e){
var leaveList = Object.keys(this.pseudoTrackers),
linkedKeys = {
"row":["cell"],
"cell":["row"],
};
leaveList = leaveList.filter((item) => {
var links = linkedKeys[key];
return item !== key && (!links || (links && !links.includes(item)));
});
leaveList.forEach((key) => {
var target = this.pseudoTrackers[key].target;
if(this.pseudoTrackers[key].target){
this.dispatch(key + "-mouseleave", e, target);
this.pseudoTrackers[key].target = null;
}
});
}
bindSubscriptionWatchers(){
var listeners = Object.keys(this.listeners),
components = Object.values(this.componentMap);
for(let comp of components){
for(let listener of listeners){
let key = comp + "-" + listener;
this.subscriptionChange(key, this.subscriptionChanged.bind(this, comp, listener));
}
}
this.subscribe("table-destroy", this.clearWatchers.bind(this));
}
subscriptionChanged(component, key, added){
var listener = this.listeners[key].components,
index = listener.indexOf(component),
changed = false;
if(added){
if(index === -1){
listener.push(component);
changed = true;
}
}else {
if(!this.subscribed(component + "-" + key)){
if(index > -1){
listener.splice(index, 1);
changed = true;
}
}
}
if((key === "mouseenter" || key === "mouseleave") && !this.pseudoTracking){
this.bindPseudoEvents();
}
if(changed){
this.updateEventListeners();
}
}
updateEventListeners(){
for(let key in this.listeners){
let listener = this.listeners[key];
if(listener.components.length){
if(!listener.handler){
listener.handler = this.track.bind(this, key);
this.el.addEventListener(key, listener.handler);
// this.el.addEventListener(key, listener.handler, {passive: true})
}
}else {
if(listener.handler){
this.el.removeEventListener(key, listener.handler);
listener.handler = null;
}
}
}
}
track(type, e){
var path = (e.composedPath && e.composedPath()) || e.path;
var targets = this.findTargets(path);
targets = this.bindComponents(type, targets);
this.triggerEvents(type, e, targets);
if(this.pseudoTracking && (type == "mouseover" || type == "mouseleave") && !Object.keys(targets).length){
this.pseudoMouseLeave("none", e);
}
}
findTargets(path){
var targets = {};
let componentMap = Object.keys(this.componentMap);
for (let el of path) {
let classList = el.classList ? [...el.classList] : [];
let abort = classList.filter((item) => {
return this.abortClasses.includes(item);
});
if(abort.length){
break;
}
let elTargets = classList.filter((item) => {
return componentMap.includes(item);
});
for (let target of elTargets) {
if(!targets[this.componentMap[target]]){
targets[this.componentMap[target]] = el;
}
}
}
if(targets.group && targets.group === targets.row){
delete targets.row;
}
return targets;
}
bindComponents(type, targets){
//ensure row component is looked up before cell
var keys = Object.keys(targets).reverse(),
listener = this.listeners[type],
matches = {},
targetMatches = {};
for(let key of keys){
let component,
target = targets[key],
previousTarget = this.previousTargets[key];
if(previousTarget && previousTarget.target === target){
component = previousTarget.component;
}else {
switch(key){
case "row":
case "group":
if(listener.components.includes("row") || listener.components.includes("cell") || listener.components.includes("group")){
let rows = this.table.rowManager.getVisibleRows(true);
component = rows.find((row) => {
return row.getElement() === target;
});
if(targets["row"] && targets["row"].parentNode && targets["row"].parentNode.closest(".tabulator-row")){
targets[key] = false;
}
}
break;
case "column":
if(listener.components.includes("column")){
component = this.table.columnManager.findColumn(target);
}
break;
case "cell":
if(listener.components.includes("cell")){
if(matches["row"] instanceof Row){
component = matches["row"].findCell(target);
}else {
if(targets["row"]){
console.warn("Event Target Lookup Error - The row this cell is attached to cannot be found, has the table been reinitialized without being destroyed first?");
}
}
}
break;
}
}
if(component){
matches[key] = component;
targetMatches[key] = {
target:target,
component:component,
};
}
}
this.previousTargets = targetMatches;
return matches;
}
triggerEvents(type, e, targets){
var listener = this.listeners[type];
for(let key in targets){
if(targets[key] && listener.components.includes(key)){
this.dispatch(key + "-" + type, e, targets[key]);
}
}
}
clearWatchers(){
for(let key in this.listeners){
let listener = this.listeners[key];
if(listener.handler){
this.el.removeEventListener(key, listener.handler);
listener.handler = null;
}
}
}
}
class ComponentFunctionBinder{
constructor(table){
this.table = table;
this.bindings = {};
}
bind(type, funcName, handler){
if(!this.bindings[type]){
this.bindings[type] = {};
}
if(this.bindings[type][funcName]){
console.warn("Unable to bind component handler, a matching function name is already bound", type, funcName, handler);
}else {
this.bindings[type][funcName] = handler;
}
}
handle(type, component, name){
if(this.bindings[type] && this.bindings[type][name] && typeof this.bindings[type][name].bind === 'function'){
return this.bindings[type][name].bind(null, component);
}else {
if(name !== "then" && typeof name === "string" && !name.startsWith("_")){
if(this.table.options.debugInvalidComponentFuncs){
console.error("The " + type + " component does not have a " + name + " function, have you checked that you have the correct Tabulator module installed?");
}
}
}
}
}
class DataLoader extends CoreFeature{
constructor(table){
super(table);
this.requestOrder = 0; //prevent requests coming out of sequence if overridden by another load request
this.loading = false;
}
initialize(){}
load(data, params, config, replace, silent, columnsChanged){
var requestNo = ++this.requestOrder;
this.dispatchExternal("dataLoading", data);
//parse json data to array
if (data && (data.indexOf("{") == 0 || data.indexOf("[") == 0)){
data = JSON.parse(data);
}
if(this.confirm("data-loading", [data, params, config, silent])){
this.loading = true;
if(!silent){
this.alertLoader();
}
//get params for request
params = this.chain("data-params", [data, config, silent], params || {}, params || {});
params = this.mapParams(params, this.table.options.dataSendParams);
var result = this.chain("data-load", [data, params, config, silent], false, Promise.resolve([]));
return result.then((response) => {
if(!Array.isArray(response) && typeof response == "object"){
response = this.mapParams(response, this.objectInvert(this.table.options.dataReceiveParams));
}
var rowData = this.chain("data-loaded", response, null, response);
if(requestNo == this.requestOrder){
this.clearAlert();
if(rowData !== false){
this.dispatchExternal("dataLoaded", rowData);
this.table.rowManager.setData(rowData, replace, typeof columnsChanged === "undefined" ? !replace : columnsChanged);
}
}else {
console.warn("Data Load Response Blocked - An active data load request was blocked by an attempt to change table data while the request was being made");
}
}).catch((error) => {
console.error("Data Load Error: ", error);
this.dispatchExternal("dataLoadError", error);
if(!silent){
this.alertError();
}
setTimeout(() => {
this.clearAlert();
}, this.table.options.dataLoaderErrorTimeout);
})
.finally(() => {
this.loading = false;
});
}else {
this.dispatchExternal("dataLoaded", data);
if(!data){
data = [];
}
this.table.rowManager.setData(data, replace, typeof columnsChanged === "undefined" ? !replace : columnsChanged);
return Promise.resolve();
}
}
mapParams(params, map){
var output = {};
for(let key in params){
output[map.hasOwnProperty(key) ? map[key] : key] = params[key];
}
return output;
}
objectInvert(obj){
var output = {};
for(let key in obj){
output[obj[key]] = key;
}
return output;
}
blockActiveLoad(){
this.requestOrder++;
}
alertLoader(){
var shouldLoad = typeof this.table.options.dataLoader === "function" ? this.table.options.dataLoader() : this.table.options.dataLoader;
if(shouldLoad){
this.table.alertManager.alert(this.table.options.dataLoaderLoading || this.langText("data|loading"));
}
}
alertError(){
this.table.alertManager.alert(this.table.options.dataLoaderError || this.langText("data|error"), "error");
}
clearAlert(){
this.table.alertManager.clear();
}
}
class ExternalEventBus {
constructor(table, optionsList, debug){
this.table = table;
this.events = {};
this.optionsList = optionsList || {};
this.subscriptionNotifiers = {};
this.dispatch = debug ? this._debugDispatch.bind(this) : this._dispatch.bind(this);
this.debug = debug;
}
subscriptionChange(key, callback){
if(!this.subscriptionNotifiers[key]){
this.subscriptionNotifiers[key] = [];
}
this.subscriptionNotifiers[key].push(callback);
if(this.subscribed(key)){
this._notifySubscriptionChange(key, true);
}
}
subscribe(key, callback){
if(!this.events[key]){
this.events[key] = [];
}
this.events[key].push(callback);
this._notifySubscriptionChange(key, true);
}
unsubscribe(key, callback){
var index;
if(this.events[key]){
if(callback){
index = this.events[key].findIndex((item) => {
return item === callback;
});
if(index > -1){
this.events[key].splice(index, 1);
}else {
console.warn("Cannot remove event, no matching event found:", key, callback);
return;
}
}else {
delete this.events[key];
}
}else {
console.warn("Cannot remove event, no events set on:", key);
return;
}
this._notifySubscriptionChange(key, false);
}
subscribed(key){
return this.events[key] && this.events[key].length;
}
_notifySubscriptionChange(key, subscribed){
var notifiers = this.subscriptionNotifiers[key];
if(notifiers){
notifiers.forEach((callback)=>{
callback(subscribed);
});
}
}
_dispatch(){
var args = Array.from(arguments),
key = args.shift(),
result;
if(this.events[key]){
this.events[key].forEach((callback, i) => {
let callResult = callback.apply(this.table, args);
if(!i){
result = callResult;
}
});
}
return result;
}
_debugDispatch(){
var args = Array.from(arguments),
key = args[0];
args[0] = "ExternalEvent:" + args[0];
if(this.debug === true || this.debug.includes(key)){
console.log(...args);
}
return this._dispatch(...arguments);
}
}
class InternalEventBus {
constructor(debug){
this.events = {};
this.subscriptionNotifiers = {};
this.dispatch = debug ? this._debugDispatch.bind(this) : this._dispatch.bind(this);
this.chain = debug ? this._debugChain.bind(this) : this._chain.bind(this);
this.confirm = debug ? this._debugConfirm.bind(this) : this._confirm.bind(this);
this.debug = debug;
}
subscriptionChange(key, callback){
if(!this.subscriptionNotifiers[key]){
this.subscriptionNotifiers[key] = [];
}
this.subscriptionNotifiers[key].push(callback);
if(this.subscribed(key)){
this._notifySubscriptionChange(key, true);
}
}
subscribe(key, callback, priority = 10000){
if(!this.events[key]){
this.events[key] = [];
}
this.events[key].push({callback, priority});
this.events[key].sort((a, b) => {
return a.priority - b.priority;
});
this._notifySubscriptionChange(key, true);
}
unsubscribe(key, callback){
var index;
if(this.events[key]){
if(callback){
index = this.events[key].findIndex((item) => {
return item.callback === callback;
});
if(index > -1){
this.events[key].splice(index, 1);
}else {
console.warn("Cannot remove event, no matching event found:", key, callback);
return;
}
}
}else {
console.warn("Cannot remove event, no events set on:", key);
return;
}
this._notifySubscriptionChange(key, false);
}
subscribed(key){
return this.events[key] && this.events[key].length;
}
_chain(key, args, initialValue, fallback){
var value = initialValue;
if(!Array.isArray(args)){
args = [args];
}
if(this.subscribed(key)){
this.events[key].forEach((subscriber, i) => {
value = subscriber.callback.apply(this, args.concat([value]));
});
return value;
}else {
return typeof fallback === "function" ? fallback() : fallback;
}
}
_confirm(key, args){
var confirmed = false;
if(!Array.isArray(args)){
args = [args];
}
if(this.subscribed(key)){
this.events[key].forEach((subscriber, i) => {
if(subscriber.callback.apply(this, args)){
confirmed = true;
}
});
}
return confirmed;
}
_notifySubscriptionChange(key, subscribed){
var notifiers = this.subscriptionNotifiers[key];
if(notifiers){
notifiers.forEach((callback)=>{
callback(subscribed);
});
}
}
_dispatch(){
var args = Array.from(arguments),
key = args.shift();
if(this.events[key]){
this.events[key].forEach((subscriber) => {
subscriber.callback.apply(this, args);
});
}
}
_debugDispatch(){
var args = Array.from(arguments),
key = args[0];
args[0] = "InternalEvent:" + key;
if(this.debug === true || this.debug.includes(key)){
console.log(...args);
}
return this._dispatch(...arguments);
}
_debugChain(){
var args = Array.from(arguments),
key = args[0];
args[0] = "InternalEvent:" + key;
if(this.debug === true || this.debug.includes(key)){
console.log(...args);
}
return this._chain(...arguments);
}
_debugConfirm(){
var args = Array.from(arguments),
key = args[0];
args[0] = "InternalEvent:" + key;
if(this.debug === true || this.debug.includes(key)){
console.log(...args);
}
return this._confirm(...arguments);
}
}
class DeprecationAdvisor extends CoreFeature{
constructor(table){
super(table);
}
_warnUser(){
if(this.options("debugDeprecation")){
console.warn(...arguments);
}
}
check(oldOption, newOption){
var msg = "";
if(typeof this.options(oldOption) !== "undefined"){
msg = "Deprecated Setup Option - Use of the %c" + oldOption + "%c option is now deprecated";
if(newOption){
msg = msg + ", Please use the %c" + newOption + "%c option instead";
this._warnUser(msg, 'font-weight: bold;', 'font-weight: normal;', 'font-weight: bold;', 'font-weight: normal;');
}else {
this._warnUser(msg, 'font-weight: bold;', 'font-weight: normal;');
}
return false;
}else {
return true;
}
}
checkMsg(oldOption, msg){
if(typeof this.options(oldOption) !== "undefined"){
this._warnUser("%cDeprecated Setup Option - Use of the %c" + oldOption + " %c option is now deprecated, " + msg, 'font-weight: normal;', 'font-weight: bold;', 'font-weight: normal;');
return false;
}else {
return true;
}
}
msg(msg){
this._warnUser(msg);
}
}
class TableRegistry {
static register(table){
TableRegistry.tables.push(table);
}
static deregister(table){
var index = TableRegistry.tables.indexOf(table);
if(index > -1){
TableRegistry.tables.splice(index, 1);
}
}
static lookupTable(query, silent){
var results = [],
matches, match;
if(typeof query === "string"){
matches = document.querySelectorAll(query);
if(matches.length){
for(var i = 0; i < matches.length; i++){
match = TableRegistry.matchElement(matches[i]);
if(match){
results.push(match);
}
}
}
}else if((typeof HTMLElement !== "undefined" && query instanceof HTMLElement) || query instanceof Tabulator){
match = TableRegistry.matchElement(query);
if(match){
results.push(match);
}
}else if(Array.isArray(query)){
query.forEach(function(item){
results = results.concat(TableRegistry.lookupTable(item));
});
}else {
if(!silent){
console.warn("Table Connection Error - Invalid Selector", query);
}
}
return results;
}
static matchElement(element){
return TableRegistry.tables.find(function(table){
return element instanceof Tabulator ? table === element : table.element === element;
});
}
}
TableRegistry.tables = [];
//resize columns to fit data they contain
function fitData(columns, forced){
if(forced){
this.table.columnManager.renderer.reinitializeColumnWidths(columns);
}
if(this.table.options.responsiveLayout && this.table.modExists("responsiveLayout", true)){
this.table.modules.responsiveLayout.update();
}
}
//resize columns to fit data they contain and stretch row to fill table, also used for fitDataTable
function fitDataGeneral(columns, forced){
columns.forEach(function(column){
column.reinitializeWidth();
});
if(this.table.options.responsiveLayout && this.table.modExists("responsiveLayout", true)){
this.table.modules.responsiveLayout.update();
}
}
//resize columns to fit data the contain and stretch last column to fill table
function fitDataStretch(columns, forced){
var colsWidth = 0,
tableWidth = this.table.rowManager.element.clientWidth,
gap = 0,
lastCol = false;
columns.forEach((column, i) => {
if(!column.widthFixed){
column.reinitializeWidth();
}
if(this.table.options.responsiveLayout ? column.modules.responsive.visible : column.visible){
lastCol = column;
}
if(column.visible){
colsWidth += column.getWidth();
}
});
if(lastCol){
gap = tableWidth - colsWidth + lastCol.getWidth();
if(this.table.options.responsiveLayout && this.table.modExists("responsiveLayout", true)){
lastCol.setWidth(0);
this.table.modules.responsiveLayout.update();
}
if(gap > 0){
lastCol.setWidth(gap);
}else {
lastCol.reinitializeWidth();
}
}else {
if(this.table.options.responsiveLayout && this.table.modExists("responsiveLayout", true)){
this.table.modules.responsiveLayout.update();
}
}
}
//resize columns to fit
function fitColumns(columns, forced){
var totalWidth = this.table.rowManager.element.getBoundingClientRect().width; //table element width
var fixedWidth = 0; //total width of columns with a defined width
var flexWidth = 0; //total width available to flexible columns
var flexGrowUnits = 0; //total number of widthGrow blocks across all columns
var flexColWidth = 0; //desired width of flexible columns
var flexColumns = []; //array of flexible width columns
var fixedShrinkColumns = []; //array of fixed width columns that can shrink
var flexShrinkUnits = 0; //total number of widthShrink blocks across all columns
var overflowWidth = 0; //horizontal overflow width
var gapFill = 0; //number of pixels to be added to final column to close and half pixel gaps
function calcWidth(width){
var colWidth;
if(typeof(width) == "string"){
if(width.indexOf("%") > -1){
colWidth = (totalWidth / 100) * parseInt(width);
}else {
colWidth = parseInt(width);
}
}else {
colWidth = width;
}
return colWidth;
}
//ensure columns resize to take up the correct amount of space
function scaleColumns(columns, freeSpace, colWidth, shrinkCols){
var oversizeCols = [],
oversizeSpace = 0,
remainingSpace = 0,
nextColWidth = 0,
remainingFlexGrowUnits = flexGrowUnits,
gap = 0,
changeUnits = 0,
undersizeCols = [];
function calcGrow(col){
return (colWidth * (col.column.definition.widthGrow || 1));
}
function calcShrink(col){
return (calcWidth(col.width) - (colWidth * (col.column.definition.widthShrink || 0)));
}
columns.forEach(function(col, i){
var width = shrinkCols ? calcShrink(col) : calcGrow(col);
if(col.column.minWidth >= width){
oversizeCols.push(col);
}else {
if(col.column.maxWidth && col.column.maxWidth < width){
col.width = col.column.maxWidth;
freeSpace -= col.column.maxWidth;
remainingFlexGrowUnits -= shrinkCols ? (col.column.definition.widthShrink || 1) : (col.column.definition.widthGrow || 1);
if(remainingFlexGrowUnits){
colWidth = Math.floor(freeSpace/remainingFlexGrowUnits);
}
}else {
undersizeCols.push(col);
changeUnits += shrinkCols ? (col.column.definition.widthShrink || 1) : (col.column.definition.widthGrow || 1);
}
}
});
if(oversizeCols.length){
oversizeCols.forEach(function(col){
oversizeSpace += shrinkCols ? col.width - col.column.minWidth : col.column.minWidth;
col.width = col.column.minWidth;
});
remainingSpace = freeSpace - oversizeSpace;
nextColWidth = changeUnits ? Math.floor(remainingSpace/changeUnits) : remainingSpace;
gap = scaleColumns(undersizeCols, remainingSpace, nextColWidth, shrinkCols);
}else {
gap = changeUnits ? freeSpace - (Math.floor(freeSpace/changeUnits) * changeUnits) : freeSpace;
undersizeCols.forEach(function(column){
column.width = shrinkCols ? calcShrink(column) : calcGrow(column);
});
}
return gap;
}
if(this.table.options.responsiveLayout && this.table.modExists("responsiveLayout", true)){
this.table.modules.responsiveLayout.update();
}
//adjust for vertical scrollbar if present
if(this.table.rowManager.element.scrollHeight > this.table.rowManager.element.clientHeight){
totalWidth -= this.table.rowManager.element.offsetWidth - this.table.rowManager.element.clientWidth;
}
columns.forEach(function(column){
var width, minWidth, colWidth;
if(column.visible){
width = column.definition.width;
minWidth = parseInt(column.minWidth);
if(width){
colWidth = calcWidth(width);
fixedWidth += colWidth > minWidth ? colWidth : minWidth;
if(column.definition.widthShrink){
fixedShrinkColumns.push({
column:column,
width:colWidth > minWidth ? colWidth : minWidth
});
flexShrinkUnits += column.definition.widthShrink;
}
}else {
flexColumns.push({
column:column,
width:0,
});
flexGrowUnits += column.definition.widthGrow || 1;
}
}
});
//calculate available space
flexWidth = totalWidth - fixedWidth;
//calculate correct column size
flexColWidth = Math.floor(flexWidth / flexGrowUnits);
//generate column widths
gapFill = scaleColumns(flexColumns, flexWidth, flexColWidth, false);
//increase width of last column to account for rounding errors
if(flexColumns.length && gapFill > 0){
flexColumns[flexColumns.length-1].width += gapFill;
}
//calculate space for columns to be shrunk into
flexColumns.forEach(function(col){
flexWidth -= col.width;
});
overflowWidth = Math.abs(gapFill) + flexWidth;
//shrink oversize columns if there is no available space
if(overflowWidth > 0 && flexShrinkUnits){
gapFill = scaleColumns(fixedShrinkColumns, overflowWidth, Math.floor(overflowWidth / flexShrinkUnits), true);
}
//decrease width of last column to account for rounding errors
if(gapFill && fixedShrinkColumns.length){
fixedShrinkColumns[fixedShrinkColumns.length-1].width -= gapFill;
}
flexColumns.forEach(function(col){
col.column.setWidth(col.width);
});
fixedShrinkColumns.forEach(function(col){
col.column.setWidth(col.width);
});
}
var defaultModes = {
fitData:fitData,
fitDataFill:fitDataGeneral,
fitDataTable:fitDataGeneral,
fitDataStretch:fitDataStretch,
fitColumns:fitColumns ,
};
class Layout extends Module{
constructor(table){
super(table, "layout");
this.mode = null;
this.registerTableOption("layout", "fitData"); //layout type
this.registerTableOption("layoutColumnsOnNewData", false); //update column widths on setData
this.registerColumnOption("widthGrow");
this.registerColumnOption("widthShrink");
}
//initialize layout system
initialize(){
var layout = this.table.options.layout;
if(Layout.modes[layout]){
this.mode = layout;
}else {
console.warn("Layout Error - invalid mode set, defaulting to 'fitData' : " + layout);
this.mode = 'fitData';
}
this.table.element.setAttribute("tabulator-layout", this.mode);
}
getMode(){
return this.mode;
}
//trigger table layout
layout(dataChanged){
this.dispatch("layout-refreshing");
Layout.modes[this.mode].call(this, this.table.columnManager.columnsByIndex, dataChanged);
this.dispatch("layout-refreshed");
}
}
Layout.moduleName = "layout";
//load defaults
Layout.modes = defaultModes;
var defaultLangs = {
"default":{ //hold default locale text
"groups":{
"item":"item",
"items":"items",
},
"columns":{
},
"data":{
"loading":"Loading",
"error":"Error",
},
"pagination":{
"page_size":"Page Size",
"page_title":"Show Page",
"first":"First",
"first_title":"First Page",
"last":"Last",
"last_title":"Last Page",
"prev":"Prev",
"prev_title":"Prev Page",
"next":"Next",
"next_title":"Next Page",
"all":"All",
"counter":{
"showing": "Showing",
"of": "of",
"rows": "rows",
"pages": "pages",
}
},
"headerFilters":{
"default":"filter column...",
"columns":{}
}
},
};
class Localize extends Module{
constructor(table){
super(table);
this.locale = "default"; //current locale
this.lang = false; //current language
this.bindings = {}; //update events to call when locale is changed
this.langList = {};
this.registerTableOption("locale", false); //current system language
this.registerTableOption("langs", {});
}
initialize(){
this.langList = Helpers.deepClone(Localize.langs);
if(this.table.options.columnDefaults.headerFilterPlaceholder !== false){
this.setHeaderFilterPlaceholder(this.table.options.columnDefaults.headerFilterPlaceholder);
}
for(let locale in this.table.options.langs){
this.installLang(locale, this.table.options.langs[locale]);
}
this.setLocale(this.table.options.locale);
this.registerTableFunction("setLocale", this.setLocale.bind(this));
this.registerTableFunction("getLocale", this.getLocale.bind(this));
this.registerTableFunction("getLang", this.getLang.bind(this));
}
//set header placeholder
setHeaderFilterPlaceholder(placeholder){
this.langList.default.headerFilters.default = placeholder;
}
//setup a lang description object
installLang(locale, lang){
if(this.langList[locale]){
this._setLangProp(this.langList[locale], lang);
}else {
this.langList[locale] = lang;
}
}
_setLangProp(lang, values){
for(let key in values){
if(lang[key] && typeof lang[key] == "object"){
this._setLangProp(lang[key], values[key]);
}else {
lang[key] = values[key];
}
}
}
//set current locale
setLocale(desiredLocale){
desiredLocale = desiredLocale || "default";
//fill in any matching language values
function traverseLang(trans, path){
for(var prop in trans){
if(typeof trans[prop] == "object"){
if(!path[prop]){
path[prop] = {};
}
traverseLang(trans[prop], path[prop]);
}else {
path[prop] = trans[prop];
}
}
}
//determining correct locale to load
if(desiredLocale === true && navigator.language){
//get local from system
desiredLocale = navigator.language.toLowerCase();
}
if(desiredLocale){
//if locale is not set, check for matching top level locale else use default
if(!this.langList[desiredLocale]){
let prefix = desiredLocale.split("-")[0];
if(this.langList[prefix]){
console.warn("Localization Error - Exact matching locale not found, using closest match: ", desiredLocale, prefix);
desiredLocale = prefix;
}else {
console.warn("Localization Error - Matching locale not found, using default: ", desiredLocale);
desiredLocale = "default";
}
}
}
this.locale = desiredLocale;
//load default lang template
this.lang = Helpers.deepClone(this.langList.default || {});
if(desiredLocale != "default"){
traverseLang(this.langList[desiredLocale], this.lang);
}
this.dispatchExternal("localized", this.locale, this.lang);
this._executeBindings();
}
//get current locale
getLocale(locale){
return this.locale;
}
//get lang object for given local or current if none provided
getLang(locale){
return locale ? this.langList[locale] : this.lang;
}
//get text for current locale
getText(path, value){
var fillPath = value ? path + "|" + value : path,
pathArray = fillPath.split("|"),
text = this._getLangElement(pathArray, this.locale);
// if(text === false){
// console.warn("Localization Error - Matching localized text not found for given path: ", path);
// }
return text || "";
}
//traverse langs object and find localized copy
_getLangElement(path, locale){
var root = this.lang;
path.forEach(function(level){
var rootPath;
if(root){
rootPath = root[level];
if(typeof rootPath != "undefined"){
root = rootPath;
}else {
root = false;
}
}
});
return root;
}
//set update binding
bind(path, callback){
if(!this.bindings[path]){
this.bindings[path] = [];
}
this.bindings[path].push(callback);
callback(this.getText(path), this.lang);
}
//iterate through bindings and trigger updates
_executeBindings(){
for(let path in this.bindings){
this.bindings[path].forEach((binding) => {
binding(this.getText(path), this.lang);
});
}
}
}
Localize.moduleName = "localize";
//load defaults
Localize.langs = defaultLangs;
class Comms extends Module{
constructor(table){
super(table);
}
initialize(){
this.registerTableFunction("tableComms", this.receive.bind(this));
}
getConnections(selectors){
var connections = [],
connection;
connection = TableRegistry.lookupTable(selectors);
connection.forEach((con) =>{
if(this.table !== con){
connections.push(con);
}
});
return connections;
}
send(selectors, module, action, data){
var connections = this.getConnections(selectors);
connections.forEach((connection) => {
connection.tableComms(this.table.element, module, action, data);
});
if(!connections.length && selectors){
console.warn("Table Connection Error - No tables matching selector found", selectors);
}
}
receive(table, module, action, data){
if(this.table.modExists(module)){
return this.table.modules[module].commsReceived(table, action, data);
}else {
console.warn("Inter-table Comms Error - no such module:", module);
}
}
}
Comms.moduleName = "comms";
var coreModules = /*#__PURE__*/Object.freeze({
__proto__: null,
LayoutModule: Layout,
LocalizeModule: Localize,
CommsModule: Comms
});
class ModuleBinder {
constructor(tabulator, modules){
this.bindStaticFunctionality(tabulator);
this.bindModules(tabulator, coreModules, true);
if(modules){
this.bindModules(tabulator, modules);
}
}
bindStaticFunctionality(tabulator){
tabulator.moduleBindings = {};
tabulator.extendModule = function(name, property, values){
if(tabulator.moduleBindings[name]){
var source = tabulator.moduleBindings[name][property];
if(source){
if(typeof values == "object"){
for(let key in values){
source[key] = values[key];
}
}else {
console.warn("Module Error - Invalid value type, it must be an object");
}
}else {
console.warn("Module Error - property does not exist:", property);
}
}else {
console.warn("Module Error - module does not exist:", name);
}
};
tabulator.registerModule = function(modules){
if(!Array.isArray(modules)){
modules = [modules];
}
modules.forEach((mod) => {
tabulator.registerModuleBinding(mod);
});
};
tabulator.registerModuleBinding = function(mod){
tabulator.moduleBindings[mod.moduleName] = mod;
};
tabulator.findTable = function(query){
var results = TableRegistry.lookupTable(query, true);
return Array.isArray(results) && !results.length ? false : results;
};
//ensure that module are bound to instantiated function
tabulator.prototype.bindModules = function(){
var orderedStartMods = [],
orderedEndMods = [],
unOrderedMods = [];
this.modules = {};
for(var name in tabulator.moduleBindings){
let mod = tabulator.moduleBindings[name];
let module = new mod(this);
this.modules[name] = module;
if(mod.prototype.moduleCore){
this.modulesCore.push(module);
}else {
if(mod.moduleInitOrder){
if(mod.moduleInitOrder < 0){
orderedStartMods.push(module);
}else {
orderedEndMods.push(module);
}
}else {
unOrderedMods.push(module);
}
}
}
orderedStartMods.sort((a, b) => a.moduleInitOrder > b.moduleInitOrder ? 1 : -1);
orderedEndMods.sort((a, b) => a.moduleInitOrder > b.moduleInitOrder ? 1 : -1);
this.modulesRegular = orderedStartMods.concat(unOrderedMods.concat(orderedEndMods));
};
}
bindModules(tabulator, modules, core){
var mods = Object.values(modules);
if(core){
mods.forEach((mod) => {
mod.prototype.moduleCore = true;
});
}
tabulator.registerModule(mods);
}
}
class Alert extends CoreFeature{
constructor(table){
super(table);
this.element = this._createAlertElement();
this.msgElement = this._createMsgElement();
this.type = null;
this.element.appendChild(this.msgElement);
}
_createAlertElement(){
var el = document.createElement("div");
el.classList.add("tabulator-alert");
return el;
}
_createMsgElement(){
var el = document.createElement("div");
el.classList.add("tabulator-alert-msg");
el.setAttribute("role", "alert");
return el;
}
_typeClass(){
return "tabulator-alert-state-" + this.type;
}
alert(content, type = "msg"){
if(content){
this.clear();
this.type = type;
while(this.msgElement.firstChild) this.msgElement.removeChild(this.msgElement.firstChild);
this.msgElement.classList.add(this._typeClass());
if(typeof content === "function"){
content = content();
}
if(content instanceof HTMLElement){
this.msgElement.appendChild(content);
}else {
this.msgElement.innerHTML = content;
}
this.table.element.appendChild(this.element);
}
}
clear(){
if(this.element.parentNode){
this.element.parentNode.removeChild(this.element);
}
this.msgElement.classList.remove(this._typeClass());
}
}
class Tabulator {
constructor(element, options){
this.options = {};
this.columnManager = null; // hold Column Manager
this.rowManager = null; //hold Row Manager
this.footerManager = null; //holder Footer Manager
this.alertManager = null; //hold Alert Manager
this.vdomHoz = null; //holder horizontal virtual dom
this.externalEvents = null; //handle external event messaging
this.eventBus = null; //handle internal event messaging
this.interactionMonitor = false; //track user interaction
this.browser = ""; //hold current browser type
this.browserSlow = false; //handle reduced functionality for slower browsers
this.browserMobile = false; //check if running on mobile, prevent resize cancelling edit on keyboard appearance
this.rtl = false; //check if the table is in RTL mode
this.originalElement = null; //hold original table element if it has been replaced
this.componentFunctionBinder = new ComponentFunctionBinder(this); //bind component functions
this.dataLoader = false; //bind component functions
this.modules = {}; //hold all modules bound to this table
this.modulesCore = []; //hold core modules bound to this table (for initialization purposes)
this.modulesRegular = []; //hold regular modules bound to this table (for initialization purposes)
this.deprecationAdvisor = new DeprecationAdvisor(this);
this.optionsList = new OptionsList(this, "table constructor");
this.initialized = false;
this.destroyed = false;
if(this.initializeElement(element)){
this.initializeCoreSystems(options);
//delay table creation to allow event bindings immediately after the constructor
setTimeout(() => {
this._create();
});
}
TableRegistry.register(this); //register table for inter-device communication
}
initializeElement(element){
if(typeof HTMLElement !== "undefined" && element instanceof HTMLElement){
this.element = element;
return true;
}else if(typeof element === "string"){
this.element = document.querySelector(element);
if(this.element){
return true;
}else {
console.error("Tabulator Creation Error - no element found matching selector: ", element);
return false;
}
}else {
console.error("Tabulator Creation Error - Invalid element provided:", element);
return false;
}
}
initializeCoreSystems(options){
this.columnManager = new ColumnManager(this);
this.rowManager = new RowManager(this);
this.footerManager = new FooterManager(this);
this.dataLoader = new DataLoader(this);
this.alertManager = new Alert(this);
this.bindModules();
this.options = this.optionsList.generate(Tabulator.defaultOptions, options);
this._clearObjectPointers();
this._mapDeprecatedFunctionality();
this.externalEvents = new ExternalEventBus(this, this.options, this.options.debugEventsExternal);
this.eventBus = new InternalEventBus(this.options.debugEventsInternal);
this.interactionMonitor = new InteractionManager(this);
this.dataLoader.initialize();
// this.columnManager.initialize();
// this.rowManager.initialize();
this.footerManager.initialize();
}
//convert deprecated functionality to new functions
_mapDeprecatedFunctionality(){
//all previously deprecated functionality removed in the 5.0 release
}
_clearSelection(){
this.element.classList.add("tabulator-block-select");
if (window.getSelection) {
if (window.getSelection().empty) { // Chrome
window.getSelection().empty();
} else if (window.getSelection().removeAllRanges) { // Firefox
window.getSelection().removeAllRanges();
}
} else if (document.selection) { // IE?
document.selection.empty();
}
this.element.classList.remove("tabulator-block-select");
}
//create table
_create(){
this.externalEvents.dispatch("tableBuilding");
this.eventBus.dispatch("table-building");
this._rtlCheck();
this._buildElement();
this._initializeTable();
this._loadInitialData();
this.initialized = true;
this.externalEvents.dispatch("tableBuilt");
}
_rtlCheck(){
var style = window.getComputedStyle(this.element);
switch(this.options.textDirection){
case"auto":
if(style.direction !== "rtl"){
break;
}
case "rtl":
this.element.classList.add("tabulator-rtl");
this.rtl = true;
break;
case "ltr":
this.element.classList.add("tabulator-ltr");
default:
this.rtl = false;
}
}
//clear pointers to objects in default config object
_clearObjectPointers(){
this.options.columns = this.options.columns.slice(0);
if(Array.isArray(this.options.data) && !this.options.reactiveData){
this.options.data = this.options.data.slice(0);
}
}
//build tabulator element
_buildElement(){
var element = this.element,
options = this.options,
newElement;
if(element.tagName === "TABLE"){
this.originalElement = this.element;
newElement = document.createElement("div");
//transfer attributes to new element
var attributes = element.attributes;
// loop through attributes and apply them on div
for(var i in attributes){
if(typeof attributes[i] == "object"){
newElement.setAttribute(attributes[i].name, attributes[i].value);
}
}
// replace table with div element
element.parentNode.replaceChild(newElement, element);
this.element = element = newElement;
}
element.classList.add("tabulator");
element.setAttribute("role", "grid");
//empty element
while(element.firstChild) element.removeChild(element.firstChild);
//set table height
if(options.height){
options.height = isNaN(options.height) ? options.height : options.height + "px";
element.style.height = options.height;
}
//set table min height
if(options.minHeight !== false){
options.minHeight = isNaN(options.minHeight) ? options.minHeight : options.minHeight + "px";
element.style.minHeight = options.minHeight;
}
//set table maxHeight
if(options.maxHeight !== false){
options.maxHeight = isNaN(options.maxHeight) ? options.maxHeight : options.maxHeight + "px";
element.style.maxHeight = options.maxHeight;
}
}
//initialize core systems and modules
_initializeTable(){
var element = this.element,
options = this.options;
this.interactionMonitor.initialize();
this.columnManager.initialize();
this.rowManager.initialize();
this._detectBrowser();
//initialize core modules
this.modulesCore.forEach((mod) => {
mod.initialize();
});
//build table elements
element.appendChild(this.columnManager.getElement());
element.appendChild(this.rowManager.getElement());
if(options.footerElement){
this.footerManager.activate();
}
if(options.autoColumns && options.data){
this.columnManager.generateColumnsFromRowData(this.options.data);
}
//initialize regular modules
this.modulesRegular.forEach((mod) => {
mod.initialize();
});
this.columnManager.setColumns(options.columns);
this.eventBus.dispatch("table-built");
}
_loadInitialData(){
this.dataLoader.load(this.options.data);
}
//deconstructor
destroy(){
var element = this.element;
this.destroyed = true;
TableRegistry.deregister(this); //deregister table from inter-device communication
this.eventBus.dispatch("table-destroy");
//clear row data
this.rowManager.destroy();
//clear DOM
while(element.firstChild) element.removeChild(element.firstChild);
element.classList.remove("tabulator");
this.externalEvents.dispatch("tableDestroyed");
}
_detectBrowser(){
var ua = navigator.userAgent||navigator.vendor||window.opera;
if(ua.indexOf("Trident") > -1){
this.browser = "ie";
this.browserSlow = true;
}else if(ua.indexOf("Edge") > -1){
this.browser = "edge";
this.browserSlow = true;
}else if(ua.indexOf("Firefox") > -1){
this.browser = "firefox";
this.browserSlow = false;
}else if(ua.indexOf("Mac OS") > -1){
this.browser = "safari";
this.browserSlow = false;
}else {
this.browser = "other";
this.browserSlow = false;
}
this.browserMobile = /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino|android|ipad|playbook|silk/i.test(ua)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw-(n|u)|c55\/|capi|ccwa|cdm-|cell|chtm|cldc|cmd-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc-s|devi|dica|dmob|do(c|p)o|ds(12|-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(-|_)|g1 u|g560|gene|gf-5|g-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd-(m|p|t)|hei-|hi(pt|ta)|hp( i|ip)|hs-c|ht(c(-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i-(20|go|ma)|i230|iac( |-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|-[a-w])|libw|lynx|m1-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|-([1-8]|c))|phil|pire|pl(ay|uc)|pn-2|po(ck|rt|se)|prox|psio|pt-g|qa-a|qc(07|12|21|32|60|-[2-7]|i-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h-|oo|p-)|sdk\/|se(c(-|0|1)|47|mc|nd|ri)|sgh-|shar|sie(-|m)|sk-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h-|v-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl-|tdg-|tel(i|m)|tim-|t-mo|to(pl|sh)|ts(70|m-|m3|m5)|tx-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas-|your|zeto|zte-/i.test(ua.slice(0,4));
}
initGuard(func, msg){
var stack, line;
if(this.options.debugInitialization && !this.initialized){
if(!func){
stack = new Error().stack.split("\n");
line = stack[0] == "Error" ? stack[2] : stack[1];
if(line[0] == " "){
func = line.trim().split(" ")[1].split(".")[1];
}else {
func = line.trim().split("@")[0];
}
}
console.warn("Table Not Initialized - Calling the " + func + " function before the table is initialized may result in inconsistent behavior, Please wait for the `tableBuilt` event before calling this function." + (msg ? " " + msg : ""));
}
return this.initialized;
}
////////////////// Data Handling //////////////////
//block table redrawing
blockRedraw(){
this.initGuard();
this.eventBus.dispatch("redraw-blocking");
this.rowManager.blockRedraw();
this.columnManager.blockRedraw();
this.eventBus.dispatch("redraw-blocked");
}
//restore table redrawing
restoreRedraw(){
this.initGuard();
this.eventBus.dispatch("redraw-restoring");
this.rowManager.restoreRedraw();
this.columnManager.restoreRedraw();
this.eventBus.dispatch("redraw-restored");
}
//load data
setData(data, params, config){
this.initGuard(false, "To set initial data please use the 'data' property in the table constructor.");
return this.dataLoader.load(data, params, config, false);
}
//clear data
clearData(){
this.initGuard();
this.dataLoader.blockActiveLoad();
this.rowManager.clearData();
}
//get table data array
getData(active){
return this.rowManager.getData(active);
}
//get table data array count
getDataCount(active){
return this.rowManager.getDataCount(active);
}
//replace data, keeping table in position with same sort
replaceData(data, params, config){
this.initGuard();
return this.dataLoader.load(data, params, config, true, true);
}
//update table data
updateData(data){
var responses = 0;
this.initGuard();
return new Promise((resolve, reject) => {
this.dataLoader.blockActiveLoad();
if(typeof data === "string"){
data = JSON.parse(data);
}
if(data && data.length > 0){
data.forEach((item) => {
var row = this.rowManager.findRow(item[this.options.index]);
if(row){
responses++;
row.updateData(item)
.then(()=>{
responses--;
if(!responses){
resolve();
}
})
.catch((e) => {
reject("Update Error - Unable to update row", item, e);
});
}else {
reject("Update Error - Unable to find row", item);
}
});
}else {
console.warn("Update Error - No data provided");
reject("Update Error - No data provided");
}
});
}
addData(data, pos, index){
this.initGuard();
return new Promise((resolve, reject) => {
this.dataLoader.blockActiveLoad();
if(typeof data === "string"){
data = JSON.parse(data);
}
if(data){
this.rowManager.addRows(data, pos, index)
.then((rows) => {
var output = [];
rows.forEach(function(row){
output.push(row.getComponent());
});
resolve(output);
});
}else {
console.warn("Update Error - No data provided");
reject("Update Error - No data provided");
}
});
}
//update table data
updateOrAddData(data){
var rows = [],
responses = 0;
this.initGuard();
return new Promise((resolve, reject) => {
this.dataLoader.blockActiveLoad();
if(typeof data === "string"){
data = JSON.parse(data);
}
if(data && data.length > 0){
data.forEach((item) => {
var row = this.rowManager.findRow(item[this.options.index]);
responses++;
if(row){
row.updateData(item)
.then(()=>{
responses--;
rows.push(row.getComponent());
if(!responses){
resolve(rows);
}
});
}else {
this.rowManager.addRows(item)
.then((newRows)=>{
responses--;
rows.push(newRows[0].getComponent());
if(!responses){
resolve(rows);
}
});
}
});
}else {
console.warn("Update Error - No data provided");
reject("Update Error - No data provided");
}
});
}
//get row object
getRow(index){
var row = this.rowManager.findRow(index);
if(row){
return row.getComponent();
}else {
console.warn("Find Error - No matching row found:", index);
return false;
}
}
//get row object
getRowFromPosition(position){
var row = this.rowManager.getRowFromPosition(position);
if(row){
return row.getComponent();
}else {
console.warn("Find Error - No matching row found:", position);
return false;
}
}
//delete row from table
deleteRow(index){
var foundRows = [];
this.initGuard();
if(!Array.isArray(index)){
index = [index];
}
//find matching rows
for(let item of index){
let row = this.rowManager.findRow(item, true);
if(row){
foundRows.push(row);
}else {
console.error("Delete Error - No matching row found:", item);
return Promise.reject("Delete Error - No matching row found");
}
}
//sort rows into correct order to ensure smooth delete from table
foundRows.sort((a, b) => {
return this.rowManager.rows.indexOf(a) > this.rowManager.rows.indexOf(b) ? 1 : -1;
});
//delete rows
foundRows.forEach((row) =>{
row.delete();
});
this.rowManager.reRenderInPosition();
return Promise.resolve();
}
//add row to table
addRow(data, pos, index){
this.initGuard();
if(typeof data === "string"){
data = JSON.parse(data);
}
return this.rowManager.addRows(data, pos, index, true)
.then((rows)=>{
return rows[0].getComponent();
});
}
//update a row if it exists otherwise create it
updateOrAddRow(index, data){
var row = this.rowManager.findRow(index);
this.initGuard();
if(typeof data === "string"){
data = JSON.parse(data);
}
if(row){
return row.updateData(data)
.then(()=>{
return row.getComponent();
});
}else {
return this.rowManager.addRows(data)
.then((rows)=>{
return rows[0].getComponent();
});
}
}
//update row data
updateRow(index, data){
var row = this.rowManager.findRow(index);
this.initGuard();
if(typeof data === "string"){
data = JSON.parse(data);
}
if(row){
return row.updateData(data)
.then(()=>{
return Promise.resolve(row.getComponent());
});
}else {
console.warn("Update Error - No matching row found:", index);
return Promise.reject("Update Error - No matching row found");
}
}
//scroll to row in DOM
scrollToRow(index, position, ifVisible){
var row = this.rowManager.findRow(index);
if(row){
return this.rowManager.scrollToRow(row, position, ifVisible);
}else {
console.warn("Scroll Error - No matching row found:", index);
return Promise.reject("Scroll Error - No matching row found");
}
}
moveRow(from, to, after){
var fromRow = this.rowManager.findRow(from);
this.initGuard();
if(fromRow){
fromRow.moveToRow(to, after);
}else {
console.warn("Move Error - No matching row found:", from);
}
}
getRows(active){
return this.rowManager.getComponents(active);
}
//get position of row in table
getRowPosition(index){
var row = this.rowManager.findRow(index);
if(row){
return row.getPosition();
}else {
console.warn("Position Error - No matching row found:", index);
return false;
}
}
/////////////// Column Functions ///////////////
setColumns(definition){
this.initGuard(false, "To set initial columns please use the 'columns' property in the table constructor");
this.columnManager.setColumns(definition);
}
getColumns(structured){
return this.columnManager.getComponents(structured);
}
getColumn(field){
var column = this.columnManager.findColumn(field);
if(column){
return column.getComponent();
}else {
console.warn("Find Error - No matching column found:", field);
return false;
}
}
getColumnDefinitions(){
return this.columnManager.getDefinitionTree();
}
showColumn(field){
var column = this.columnManager.findColumn(field);
this.initGuard();
if(column){
column.show();
}else {
console.warn("Column Show Error - No matching column found:", field);
return false;
}
}
hideColumn(field){
var column = this.columnManager.findColumn(field);
this.initGuard();
if(column){
column.hide();
}else {
console.warn("Column Hide Error - No matching column found:", field);
return false;
}
}
toggleColumn(field){
var column = this.columnManager.findColumn(field);
this.initGuard();
if(column){
if(column.visible){
column.hide();
}else {
column.show();
}
}else {
console.warn("Column Visibility Toggle Error - No matching column found:", field);
return false;
}
}
addColumn(definition, before, field){
var column = this.columnManager.findColumn(field);
this.initGuard();
return this.columnManager.addColumn(definition, before, column)
.then((column) => {
return column.getComponent();
});
}
deleteColumn(field){
var column = this.columnManager.findColumn(field);
this.initGuard();
if(column){
return column.delete();
}else {
console.warn("Column Delete Error - No matching column found:", field);
return Promise.reject();
}
}
updateColumnDefinition(field, definition){
var column = this.columnManager.findColumn(field);
this.initGuard();
if(column){
return column.updateDefinition(definition);
}else {
console.warn("Column Update Error - No matching column found:", field);
return Promise.reject();
}
}
moveColumn(from, to, after){
var fromColumn = this.columnManager.findColumn(from),
toColumn = this.columnManager.findColumn(to);
this.initGuard();
if(fromColumn){
if(toColumn){
this.columnManager.moveColumn(fromColumn, toColumn, after);
}else {
console.warn("Move Error - No matching column found:", toColumn);
}
}else {
console.warn("Move Error - No matching column found:", from);
}
}
//scroll to column in DOM
scrollToColumn(field, position, ifVisible){
return new Promise((resolve, reject) => {
var column = this.columnManager.findColumn(field);
if(column){
return this.columnManager.scrollToColumn(column, position, ifVisible);
}else {
console.warn("Scroll Error - No matching column found:", field);
return Promise.reject("Scroll Error - No matching column found");
}
});
}
//////////// General Public Functions ////////////
//redraw list without updating data
redraw(force){
this.initGuard();
this.columnManager.redraw(force);
this.rowManager.redraw(force);
}
setHeight(height){
this.options.height = isNaN(height) ? height : height + "px";
this.element.style.height = this.options.height;
this.rowManager.initializeRenderer();
this.rowManager.redraw();
}
//////////////////// Event Bus ///////////////////
on(key, callback){
this.externalEvents.subscribe(key, callback);
}
off(key, callback){
this.externalEvents.unsubscribe(key, callback);
}
dispatchEvent(){
var args = Array.from(arguments);
args.shift();
this.externalEvents.dispatch(...arguments);
}
//////////////////// Alerts ///////////////////
alert(contents, type){
this.initGuard();
this.alertManager.alert(contents, type);
}
clearAlert(){
this.initGuard();
this.alertManager.clear();
}
////////////// Extension Management //////////////
modExists(plugin, required){
if(this.modules[plugin]){
return true;
}else {
if(required){
console.error("Tabulator Module Not Installed: " + plugin);
}
return false;
}
}
module(key){
var mod = this.modules[key];
if(!mod){
console.error("Tabulator module not installed: " + key);
}
return mod;
}
}
//default setup options
Tabulator.defaultOptions = defaultOptions;
//bind modules and static functionality
new ModuleBinder(Tabulator);
//tabulator with all modules installed
class TabulatorFull extends Tabulator {}
//bind modules and static functionality
new ModuleBinder(TabulatorFull, modules);
class PseudoRow {
constructor (type){
this.type = type;
this.element = this._createElement();
}
_createElement(){
var el = document.createElement("div");
el.classList.add("tabulator-row");
return el;
}
getElement(){
return this.element;
}
getComponent(){
return false;
}
getData(){
return {};
}
getHeight(){
return this.element.outerHeight;
}
initialize(){}
reinitialize(){}
normalizeHeight(){}
generateCells(){}
reinitializeHeight(){}
calcHeight(){}
setCellHeight(){}
clearCellHeight(){}
}
export { Accessor as AccessorModule, Ajax as AjaxModule, CalcComponent, CellComponent, Clipboard as ClipboardModule, ColumnCalcs as ColumnCalcsModule, ColumnComponent, DataTree as DataTreeModule, Download as DownloadModule, Edit$1 as EditModule, Export as ExportModule, Filter as FilterModule, Format as FormatModule, FrozenColumns as FrozenColumnsModule, FrozenRows as FrozenRowsModule, GroupComponent, GroupRows as GroupRowsModule, History as HistoryModule, HtmlTableImport as HtmlTableImportModule, Import as ImportModule, Interaction as InteractionModule, Keybindings as KeybindingsModule, Menu as MenuModule, Module, MoveColumns as MoveColumnsModule, MoveRows as MoveRowsModule, Mutator as MutatorModule, Page as PageModule, Persistence as PersistenceModule, Popup$1 as PopupModule, Print as PrintModule, PseudoRow, ReactiveData as ReactiveDataModule, Renderer, ResizeColumns as ResizeColumnsModule, ResizeRows as ResizeRowsModule, ResizeTable as ResizeTableModule, ResponsiveLayout as ResponsiveLayoutModule, RowComponent, SelectRow as SelectRowModule, Sort as SortModule, Tabulator, TabulatorFull, Tooltip as TooltipModule, Validate as ValidateModule };
//# sourceMappingURL=tabulator_esm.js.map