tabulator-tables
Version:
Interactive table generation JavaScript library
2,214 lines (1,707 loc) • 733 kB
JavaScript
/* Tabulator v6.3.1 (c) Oliver Folkerd 2025 */
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, convert){
return this.table.deprecationAdvisor.check(oldOption, newOption, convert);
}
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 retrieveNestedData(separator, field, data){
var structure = separator ? field.split(separator) : [field],
length = structure.length,
output;
for(let i = 0; i < length; i++){
data = data[structure[i]];
output = data;
if(!data){
break;
}
}
return output;
}
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;
}
}
let Popup$1 = 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
let offsetHeight = Math.max(this.container.offsetHeight, scrollTop ? this.container.scrollHeight : 0);
if((y + this.element.offsetHeight) > offsetHeight) {
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.height = 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$1(this.table, menuEl, menuContainer);
}
///////////////////////////////////
//////// Alert Management ////////
///////////////////////////////////
alert(content, type){
return this.table.alertManager.alert(content, type);
}
clearAlert(){
return this.table.alertManager.clear();
}
}
var defaultAccessors = {
rownum:function(value, data, type, params, column, row){
return row.getPosition();
}
};
class Accessor extends Module{
static moduleName = "accessor";
//load defaults
static accessors = defaultAccessors;
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;
}
}
var defaultConfig = {
method: "GET",
};
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;
}
function serializeParams(params){
var output = generateParamsList$1(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(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;
}
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(params),
form = new FormData();
output.forEach(function(item){
form.append(item.key, item.value);
});
return form;
},
},
};
class Ajax extends Module{
static moduleName = "ajax";
//load defaults
static defaultConfig = defaultConfig;
static defaultURLGenerator = urlBuilder;
static defaultLoaderPromise = defaultLoaderPromise;
static contentTypeFormatters = defaultContentTypeFormatters;
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(Object.assign({}, ajaxParams), params);
}
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();
}
}
}
var defaultPasteActions = {
replace:function(data){
return this.table.setData(data);
},
update:function(data){
return this.table.updateOrAddData(data);
},
insert:function(data){
return this.table.addData(data);
},
};
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;
}
},
};
var bindings$2 = {
copyToClipboard:["ctrl + 67", "meta + 67"],
};
var actions$2 = {
copyToClipboard:function(e){
if(!this.table.modules.edit.currentCell){
if(this.table.modExists("clipboard", true)){
this.table.modules.clipboard.copy(false, true);
}
}
},
};
var extensions$4 = {
keybindings:{
bindings:bindings$2,
actions:actions$2
},
};
class Clipboard extends Module{
static moduleName = "clipboard";
static moduleExtensions = extensions$4;
//load defaults
static pasteActions = defaultPasteActions;
static pasteParsers = defaultPasteParsers;
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.checkPasteOrigin(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;
}
checkPasteOrigin(e){
var valid = true;
var blocked = this.confirm("clipboard-paste", [e]);
if(blocked || !["DIV", "SPAN"].includes(e.target.tagName)){
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;
}
}
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(transform){
return this._cell.row.getData(transform);
}
getType(){
return "cell";
}
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");
if(this.column.isRowHeader){
this.element.classList.add("tabulator-row-header");
}
}
_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.getParentComponent();
}
_getSelf(){
return this._column;
}
scrollTo(position, ifVisible){
return this._column.table.columnManager.scrollToColumn(this._column, position, ifVisible);
}
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{
static defaultOptionList = defaultColumnOptions;
constructor(def, parent, rowHeader){
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.isGroup = false;
this.isRowHeader = rowHeader;
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.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");
if(this.isRowHeader){
el.classList.add("tabulator-row-header");
}
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.registerColu