tabulator-tables
Version:
Interactive table generation JavaScript library
1,004 lines (767 loc) • 24.6 kB
JavaScript
var Clipboard = function(table){
this.table = table;
this.mode = true;
this.copySelector = false;
this.copySelectorParams = {};
this.copyFormatter = false;
this.copyFormatterParams = {};
this.pasteParser = function(){};
this.pasteAction = function(){};
this.htmlElement = false;
this.config = {};
this.blocked = true; //block copy actions not originating from this command
};
Clipboard.prototype.initialize = function(){
var self = this;
this.mode = this.table.options.clipboard;
if(this.mode === true || this.mode === "copy"){
this.table.element.addEventListener("copy", function(e){
var data;
self.processConfig();
if(!self.blocked){
e.preventDefault();
data = self.generateContent();
if (window.clipboardData && window.clipboardData.setData) {
window.clipboardData.setData('Text', data);
} else if (e.clipboardData && e.clipboardData.setData) {
e.clipboardData.setData('text/plain', data);
if(self.htmlElement){
e.clipboardData.setData('text/html', self.htmlElement.outerHTML);
}
} else if (e.originalEvent && e.originalEvent.clipboardData.setData) {
e.originalEvent.clipboardData.setData('text/plain', data);
if(self.htmlElement){
e.originalEvent.clipboardData.setData('text/html', self.htmlElement.outerHTML);
}
}
self.table.options.clipboardCopied.call(this.table, data);
self.reset();
}
});
}
if(this.mode === true || this.mode === "paste"){
this.table.element.addEventListener("paste", function(e){
self.paste(e);
});
}
this.setPasteParser(this.table.options.clipboardPasteParser);
this.setPasteAction(this.table.options.clipboardPasteAction);
};
Clipboard.prototype.processConfig = function(){
var config = {
columnHeaders:"groups",
rowGroups:true,
columnCalcs:true,
};
if(typeof this.table.options.clipboardCopyHeader !== "undefined"){
config.columnHeaders = this.table.options.clipboardCopyHeader;
console.warn("DEPRECATION WANRING - clipboardCopyHeader option has been depricated, please use the columnHeaders property on the clipboardCopyConfig option");
}
if(this.table.options.clipboardCopyConfig){
for(var key in this.table.options.clipboardCopyConfig){
config[key] = this.table.options.clipboardCopyConfig[key];
}
}
if (config.rowGroups && this.table.options.groupBy && this.table.modExists("groupRows")){
this.config.rowGroups = true;
}
if(config.columnHeaders){
if((config.columnHeaders === "groups" || config === true) && this.table.columnManager.columns.length != this.table.columnManager.columnsByIndex.length){
this.config.columnHeaders = "groups";
}else{
this.config.columnHeaders = "columns";
}
}else{
this.config.columnHeaders = false;
}
if (config.columnCalcs && this.table.modExists("columnCalcs")){
this.config.columnCalcs = true;
}
};
Clipboard.prototype.reset = function(){
this.blocked = false;
this.originalSelectionText = "";
};
Clipboard.prototype.setPasteAction = function(action){
switch(typeof action){
case "string":
this.pasteAction = this.pasteActions[action];
if(!this.pasteAction){
console.warn("Clipboard Error - No such paste action found:", action);
}
break;
case "function":
this.pasteAction = action;
break;
}
};
Clipboard.prototype.setPasteParser = function(parser){
switch(typeof parser){
case "string":
this.pasteParser = this.pasteParsers[parser];
if(!this.pasteParser){
console.warn("Clipboard Error - No such paste parser found:", parser);
}
break;
case "function":
this.pasteParser = parser;
break;
}
};
Clipboard.prototype.paste = function(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.table.options.clipboardPasted.call(this.table, data, rowData, rows);
}else{
this.table.options.clipboardPasteError.call(this.table, data);
}
}
};
Clipboard.prototype.mutateData = function(data){
var self = this,
output = [];
if(Array.isArray(data)){
data.forEach(function(row){
output.push(self.table.modules.mutator.transformRow(row, "clipboard"));
});
}else{
output = data;
}
return output;
};
Clipboard.prototype.checkPaseOrigin = function(e){
var valid = true;
if(e.target.tagName != "DIV" || this.table.modules.edit.currentCell){
valid = false;
}
return valid;
};
Clipboard.prototype.getPasteData = function(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.prototype.copy = function(selector, selectorParams, formatter, formatterParams, internal){
var range, sel, textRange;
this.blocked = false;
if(this.mode === true || this.mode === "copy"){
if (typeof window.getSelection != "undefined" && typeof document.createRange != "undefined") {
range = document.createRange();
range.selectNodeContents(this.table.element);
sel = window.getSelection();
if(sel.toString() && internal){
selector = "userSelection";
formatter = "raw";
selectorParams = 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();
}
this.setSelector(selector);
this.copySelectorParams = typeof selectorParams != "undefined" && selectorParams != null ? selectorParams : this.config.columnHeaders;
this.setFormatter(formatter);
this.copyFormatterParams = typeof formatterParams != "undefined" && formatterParams != null ? formatterParams : {};
document.execCommand('copy');
if(sel){
sel.removeAllRanges();
}
}
};
Clipboard.prototype.setSelector = function(selector){
selector = selector || this.table.options.clipboardCopySelector;
switch(typeof selector){
case "string":
if(this.copySelectors[selector]){
this.copySelector = this.copySelectors[selector];
}else{
console.warn("Clipboard Error - No such selector found:", selector);
}
break;
case "function":
this.copySelector = selector;
break;
}
};
Clipboard.prototype.setFormatter = function(formatter){
formatter = formatter || this.table.options.clipboardCopyFormatter;
switch(typeof formatter){
case "string":
if(this.copyFormatters[formatter]){
this.copyFormatter = this.copyFormatters[formatter];
}else{
console.warn("Clipboard Error - No such formatter found:", formatter);
}
break;
case "function":
this.copyFormatter = formatter;
break;
}
};
Clipboard.prototype.generateContent = function(){
var data;
this.htmlElement = false;
data = this.copySelector.call(this, this.config, this.copySelectorParams);
return this.copyFormatter.call(this, data, this.config, this.copyFormatterParams);
};
Clipboard.prototype.generateSimpleHeaders = function(columns){
var headers = [];
columns.forEach(function(column){
headers.push(column.definition.title);
});
return headers;
};
Clipboard.prototype.generateColumnGroupHeaders = function(columns){
var output = [];
this.table.columnManager.columns.forEach((column) => {
var colData = this.processColumnGroup(column);
if(colData){
output.push(colData);
}
});
return output;
};
Clipboard.prototype.processColumnGroup = function(column){
var subGroups = column.columns;
var groupData = {
type:"group",
title:column.definition.title,
column:column,
};
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(!groupData.width){
return false;
}
}else{
if(column.field && column.visible){
groupData.width = 1;
}else{
return false;
}
}
return groupData;
};
Clipboard.prototype.groupHeadersToRows = function(columns){
var headers = [];
function parseColumnGroup(column, level){
if(typeof headers[level] === "undefined"){
headers[level] = [];
}
headers[level].push(column.title);
if(column.subGroups){
column.subGroups.forEach(function(subGroup){
parseColumnGroup(subGroup, level+1);
});
}else{
padColumnheaders();
}
}
function padColumnheaders(){
var max = 0;
headers.forEach(function(title){
var len = title.length;
if(len > max){
max = len;
}
});
headers.forEach(function(title){
var len = title.length;
if(len < max){
for(var i = len; i < max; i++){
title.push("");
}
}
});
}
columns.forEach(function(column){
parseColumnGroup(column,0);
});
return headers;
};
Clipboard.prototype.rowsToData = function(rows, config, params){
var columns = this.table.columnManager.columnsByIndex,
data = [];
rows.forEach(function(row){
var rowArray = [],
rowData = row instanceof RowComponent ? row.getData("clipboard") : row;
columns.forEach(function(column){
var value = column.getFieldValue(rowData);
switch(typeof value){
case "object":
value = JSON.stringify(value);
break;
case "undefined":
case "null":
value = "";
break;
default:
value = value;
}
rowArray.push(value);
});
data.push(rowArray);
});
return data;
};
Clipboard.prototype.buildComplexRows = function(config){
var output = [],
groups = this.table.modules.groupRows.getGroups();
groups.forEach((group) => {
output.push(this.processGroupData(group));
});
return output;
};
Clipboard.prototype.processGroupData = function(group){
var subGroups = group.getSubGroups();
var groupData = {
type:"group",
key:group.key
};
if(subGroups.length){
groupData.subGroups = [];
subGroups.forEach((subGroup) => {
groupData.subGroups.push(this.processGroupData(subGroup));
});
}else{
groupData.rows = group.getRows(true);
}
return groupData;
};
Clipboard.prototype.getCalcRow = function (calcs, selector, pos){
var calcData = calcs[selector];
if(calcData){
if(pos){
calcData = calcData[pos];
}
if(Object.keys(calcData).length){
return this.rowsToData([calcData]);
}
}
return [];
};
Clipboard.prototype.buildOutput = function(rows, config, params){
var output = [],
calcs,
columns = this.table.columnManager.columnsByIndex;
if(config.columnHeaders){
if(config.columnHeaders == "groups"){
columns = this.generateColumnGroupHeaders(this.table.columnManager.columns);
output = output.concat(this.groupHeadersToRows(columns));
}else{
output.push(this.generateSimpleHeaders(columns));
}
}
if(this.config.columnCalcs){
calcs = this.table.getCalcResults();
}
//generate styled content
if(this.table.options.clipboardCopyStyled){
this.generateHTML(rows, columns, calcs, config, params);
}
//generate unstyled content
if(config.rowGroups){
rows.forEach((row) => {
output = output.concat(this.parseRowGroupData(row, config, params, calcs || {}));
});
}else{
if(config.columnCalcs){
output = output.concat(this.getCalcRow(calcs, "top"));
}
output = output.concat(this.rowsToData(rows, config, params));
if(config.columnCalcs){
output = output.concat(this.getCalcRow(calcs, "bottom"));
}
}
return output;
};
Clipboard.prototype.parseRowGroupData = function (group, config, params, calcObj){
var groupData = [];
groupData.push([group.key]);
if(group.subGroups){
group.subGroups.forEach((subGroup) => {
groupData = groupData.concat(this.parseRowGroupData(subGroup, config, params, calcObj[group.key] ? calcObj[group.key].groups || {} : {}));
});
}else{
if(config.columnCalcs){
groupData = groupData.concat(this.getCalcRow(calcObj, group.key, "top"));
}
groupData = groupData.concat(this.rowsToData(group.rows, config, params));
if(config.columnCalcs){
groupData = groupData.concat(this.getCalcRow(calcObj, group.key, "bottom"));
}
}
return groupData;
};
Clipboard.prototype.generateHTML = function (rows, columns, calcs, config, params){
var self = this,
data = [],
headers = [], body, oddRow, evenRow, calcRow, firstRow, firstCell, firstGroup, lastCell, styleCells;
//create table element
this.htmlElement = document.createElement("table");
self.mapElementStyles(this.table.element, this.htmlElement, ["border-top", "border-left", "border-right", "border-bottom"]);
function generateSimpleHeaders(){
var headerEl = document.createElement("tr");
columns.forEach(function(column){
var columnEl = document.createElement("th");
columnEl.innerHTML = column.definition.title;
self.mapElementStyles(column.getElement(), columnEl, ["border-top", "border-left", "border-right", "border-bottom", "background-color", "color", "font-weight", "font-family", "font-size"]);
headerEl.appendChild(columnEl);
});
self.mapElementStyles(self.table.columnManager.getHeadersElement(), headerEl, ["border-top", "border-left", "border-right", "border-bottom", "background-color", "color", "font-weight", "font-family", "font-size"]);
self.htmlElement.appendChild(document.createElement("thead").appendChild(headerEl));
}
function generateHeaders(headers){
var headerHolderEl = document.createElement("thead");
headers.forEach(function(columns){
var headerEl = document.createElement("tr");
columns.forEach(function(column){
var columnEl = document.createElement("th");
if(column.width > 1){
columnEl.colSpan = column.width;
}
if(column.height > 1){
columnEl.rowSpan = column.height;
}
columnEl.innerHTML = column.title;
self.mapElementStyles(column.element, columnEl, ["border-top", "border-left", "border-right", "border-bottom", "background-color", "color", "font-weight", "font-family", "font-size"]);
headerEl.appendChild(columnEl);
});
self.mapElementStyles(self.table.columnManager.getHeadersElement(), headerEl, ["border-top", "border-left", "border-right", "border-bottom", "background-color", "color", "font-weight", "font-family", "font-size"]);
headerHolderEl.appendChild(headerEl);
});
self.htmlElement.appendChild(headerHolderEl);
}
function parseColumnGroup(column, level){
if(typeof headers[level] === "undefined"){
headers[level] = [];
}
headers[level].push({
title:column.title,
width:column.width,
height:1,
children:!!column.subGroups,
element:column.column.getElement(),
});
if(column.subGroups){
column.subGroups.forEach(function(subGroup){
parseColumnGroup(subGroup, level+1);
});
}
}
function padVerticalColumnheaders(){
headers.forEach(function(row, index){
row.forEach(function(header){
if(!header.children){
header.height = headers.length - index;
}
});
});
}
function addCalcRow(calcs, selector, pos){
var calcData = calcs[selector];
if(calcData){
if(pos){
calcData = calcData[pos];
}
if(Object.keys(calcData).length){
// calcRowIndexs.push(body.length);
processRows([calcData]);
}
}
}
//create headers if needed
if(config.columnHeaders){
if(config.columnHeaders == "groups"){
columns.forEach(function(column){
parseColumnGroup(column,0);
});
padVerticalColumnheaders();
generateHeaders(headers);
}else{
generateSimpleHeaders();
}
}
columns = this.table.columnManager.columnsByIndex;
//create table body
body = document.createElement("tbody");
//lookup row styles
if(window.getComputedStyle){
oddRow = this.table.element.querySelector(".tabulator-row-odd:not(.tabulator-group):not(.tabulator-calcs)");
evenRow = this.table.element.querySelector(".tabulator-row-even:not(.tabulator-group):not(.tabulator-calcs)");
calcRow = this.table.element.querySelector(".tabulator-row.tabulator-calcs");
firstRow = this.table.element.querySelector(".tabulator-row:not(.tabulator-group):not(.tabulator-calcs)");
firstGroup = this.table.element.getElementsByClassName("tabulator-group")[0];
if(firstRow){
styleCells = firstRow.getElementsByClassName("tabulator-cell");
firstCell = styleCells[0];
lastCell = styleCells[styleCells.length - 1];
}
}
function processRows(rowArray){
//add rows to table
rowArray.forEach(function(row, i){
var rowEl = document.createElement("tr"),
styleRow = firstRow,
isCalc = false,
rowData;
if(row instanceof RowComponent){
rowData = row.getData("clipboard");
}else{
rowData = row;
isCalc = true;
}
columns.forEach(function(column, j){
var cellEl = document.createElement("td"),
value = column.getFieldValue(rowData);
switch(typeof value){
case "object":
value = JSON.stringify(value);
break;
case "undefined":
case "null":
value = "";
break;
default:
value = value;
}
cellEl.innerHTML = value;
if(column.definition.align){
cellEl.style.textAlign = column.definition.align;
}
if(j < columns.length - 1){
if(firstCell){
self.mapElementStyles(firstCell, cellEl, ["border-top", "border-left", "border-right", "border-bottom", "color", "font-weight", "font-family", "font-size"]);
}
}else{
if(firstCell){
self.mapElementStyles(firstCell, cellEl, ["border-top", "border-left", "border-right", "border-bottom", "color", "font-weight", "font-family", "font-size"]);
}
}
rowEl.appendChild(cellEl);
});
if(isCalc){
styleRow = calcRow;
}else{
if(!(i % 2) && oddRow){
styleRow = oddRow;
}
if((i % 2) && evenRow){
styleRow = evenRow;
}
}
if(styleRow){
self.mapElementStyles(styleRow, rowEl, ["border-top", "border-left", "border-right", "border-bottom", "color", "font-weight", "font-family", "font-size", "background-color"]);
}
body.appendChild(rowEl);
});
}
function processGroup(group, calcObj){
var groupEl = document.createElement("tr"),
groupCellEl = document.createElement("td");
groupCellEl.colSpan = columns.length;
groupCellEl.innerHTML = group.key;
groupEl.appendChild(groupCellEl);
body.appendChild(groupEl);
self.mapElementStyles(firstGroup, groupEl, ["border-top", "border-left", "border-right", "border-bottom", "color", "font-weight", "font-family", "font-size", "background-color"]);
if(group.subGroups){
group.subGroups.forEach((subGroup) => {
processGroup(subGroup, calcObj[group.key] ? calcObj[group.key].groups || {} : {});
});
}else{
if(config.columnCalcs){
addCalcRow(calcObj, group.key, "top");
}
processRows(group.rows);
if(config.columnCalcs){
addCalcRow(calcObj, group.key, "bottom");
}
}
}
if(config.rowGroups){
rows.forEach((group) => {
processGroup(group, calcs || {});
});
}else{
if(config.columnCalcs){
addCalcRow(calcs, "top");
}
processRows(rows);
if(config.columnCalcs){
addCalcRow(calcs, "bottom");
}
}
this.htmlElement.appendChild(body);
};
Clipboard.prototype.mapElementStyles = function(from, to, props){
var lookup = {
"background-color" : "backgroundColor",
"color" : "fontColor",
"font-weight" : "fontWeight",
"font-family" : "fontFamily",
"font-size" : "fontSize",
"border-top" : "borderTop",
"border-left" : "borderLeft",
"border-right" : "borderRight",
"border-bottom" : "borderBottom",
};
if(window.getComputedStyle){
var fromStyle = window.getComputedStyle(from);
props.forEach(function(prop){
to.style[lookup[prop]] = fromStyle.getPropertyValue(prop);
});
}
// return window.getComputedStyle ? window.getComputedStyle(element, null).getPropertyValue(property) : element.style[property.replace(/-([a-z])/g, function (g) { return g[1].toUpperCase(); })];
};
Clipboard.prototype.copySelectors = {
userSelection: function(config, params){
return params;
},
selected: function(config, params){
var rows = [];
if(this.table.modExists("selectRow", true)){
rows = this.table.modules.selectRow.getSelectedRows();
}
if(config.rowGroups){
console.warn("Clipboard Warning - select coptSelector does not support row groups");
}
return this.buildOutput(rows, config, params)
},
table: function(config, params){
if(config.rowGroups){
console.warn("Clipboard Warning - table coptSelector does not support row groups");
}
return this.buildOutput(this.table.rowManager.getComponents(), config, params);
},
active: function(config, params){
var rows;
if(config.rowGroups){
rows = this.buildComplexRows(config);
}else{
rows = this.table.rowManager.getComponents(true);
}
return this.buildOutput(rows, config, params);
},
};
Clipboard.prototype.copyFormatters = {
raw: function(data, params){
return data;
},
table: function(data, params){
var output = [];
data.forEach(function(row){
row.forEach(function(value){
if(typeof value == "undefined"){
value = "";
}
value = typeof value == "undefined" || value === null ? "" : value.toString();
if(value.match(/\r|\n/)){
value = value.split('"').join('""');
value = '"' + value + '"';
}
});
output.push(row.join("\t"));
});
return output.join("\n");
},
};
Clipboard.prototype.pasteParsers = {
table:function(clipboard){
var data = [],
success = false,
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)){
success = true;
//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;
}
}
};
Clipboard.prototype.pasteActions = {
replace:function(rows){
return this.table.setData(rows);
},
update:function(rows){
return this.table.updateOrAddData(rows);
},
insert:function(rows){
return this.table.addData(rows);
},
};
Tabulator.prototype.registerModule("clipboard", Clipboard);