jexcel
Version:
Jspreadsheet is a lightweight, vanilla javascript plugin to create amazing web-based interactive tables and spreadsheets compatible with Excel, Google Spreadsheets and any other spreadsheet software.
1,417 lines (1,281 loc) • 302 kB
JavaScript
// Jexcel core object
var jexcel = (function(el, options) {
// Create jexcel object
var obj = {};
obj.options = {};
if (! (el instanceof Element || el instanceof HTMLDocument)) {
console.error('JEXCEL: el is not a valid DOM element');
return false;
} else if (el.tagName == 'TABLE') {
if (options = jexcel.createFromTable(el, options)) {
var div = document.createElement('div');
el.parentNode.insertBefore(div, el);
el.remove();
el = div;
} else {
console.error('JEXCEL: el is not a valid DOM element');
return false;
}
}
// Loading default configuration
var defaults = {
// External data
url:null,
// Ajax options
method: 'GET',
requestVariables: null,
// Data
data:null,
// Custom sorting handler
sorting:null,
// Copy behavior
copyCompatibility:false,
root:null,
// Rows and columns definitions
rows:[],
columns:[],
// Deprected legacy options
colHeaders:[],
colWidths:[],
colAlignments:[],
nestedHeaders:null,
// Column width that is used by default
defaultColWidth:50,
defaultColAlign:'center',
// Spare rows and columns
minSpareRows:0,
minSpareCols:0,
// Minimal table dimensions
minDimensions:[0,0],
// Allow Export
allowExport:true,
// @type {boolean} - Include the header titles on download
includeHeadersOnDownload:false,
// @type {boolean} - Include the header titles on copy
includeHeadersOnCopy:false,
// Allow column sorting
columnSorting:true,
// Allow column dragging
columnDrag:false,
// Allow column resizing
columnResize:true,
// Allow row resizing
rowResize:false,
// Allow row dragging
rowDrag:true,
// Allow table edition
editable:true,
// Allow new rows
allowInsertRow:true,
// Allow new rows
allowManualInsertRow:true,
// Allow new columns
allowInsertColumn:true,
// Allow new rows
allowManualInsertColumn:true,
// Allow row delete
allowDeleteRow:true,
// Allow deleting of all rows
allowDeletingAllRows:false,
// Allow column delete
allowDeleteColumn:true,
// Allow rename column
allowRenameColumn:true,
// Allow comments
allowComments:false,
// Global wrap
wordWrap:false,
// Image options
imageOptions: null,
// CSV source
csv:null,
// Filename
csvFileName:'jexcel',
// Consider first line as header
csvHeaders:true,
// Delimiters
csvDelimiter:',',
// First row as header
parseTableFirstRowAsHeader:false,
parseTableAutoCellType:false,
// Disable corner selection
selectionCopy:true,
// Merged cells
mergeCells:{},
// Create toolbar
toolbar:null,
// Allow search
search:false,
// Create pagination
pagination:false,
paginationOptions:null,
// Full screen
fullscreen:false,
// Lazy loading
lazyLoading:false,
loadingSpin:false,
// Table overflow
tableOverflow:false,
tableHeight:'300px',
tableWidth:null,
textOverflow:false,
// Meta
meta: null,
// Style
style:null,
// Execute formulas
parseFormulas:true,
autoIncrement:true,
autoCasting:true,
// Security
secureFormulas:true,
stripHTML:true,
stripHTMLOnCopy:false,
// Filters
filters:false,
footers:null,
// Event handles
onundo:null,
onredo:null,
onload:null,
onchange:null,
oncomments:null,
onbeforechange:null,
onafterchanges:null,
onbeforeinsertrow: null,
oninsertrow:null,
onbeforeinsertcolumn: null,
oninsertcolumn:null,
onbeforedeleterow:null,
ondeleterow:null,
onbeforedeletecolumn:null,
ondeletecolumn:null,
onmoverow:null,
onmovecolumn:null,
onresizerow:null,
onresizecolumn:null,
onsort:null,
onselection:null,
oncopy:null,
onpaste:null,
onbeforepaste:null,
onmerge:null,
onfocus:null,
onblur:null,
onchangeheader:null,
oncreateeditor:null,
oneditionstart:null,
oneditionend:null,
onchangestyle:null,
onchangemeta:null,
onchangepage:null,
onbeforesave:null,
onsave:null,
// Global event dispatcher
onevent:null,
// Persistance
persistance:false,
// Customize any cell behavior
updateTable:null,
// Detach the HTML table when calling updateTable
detachForUpdates: false,
freezeColumns:null,
// Texts
text:{
noRecordsFound: 'No records found',
showingPage: 'Showing page {0} of {1} entries',
show: 'Show ',
search: 'Search',
entries: ' entries',
columnName: 'Column name',
insertANewColumnBefore: 'Insert a new column before',
insertANewColumnAfter: 'Insert a new column after',
deleteSelectedColumns: 'Delete selected columns',
renameThisColumn: 'Rename this column',
orderAscending: 'Order ascending',
orderDescending: 'Order descending',
insertANewRowBefore: 'Insert a new row before',
insertANewRowAfter: 'Insert a new row after',
deleteSelectedRows: 'Delete selected rows',
editComments: 'Edit comments',
addComments: 'Add comments',
comments: 'Comments',
clearComments: 'Clear comments',
copy: 'Copy...',
paste: 'Paste...',
saveAs: 'Save as...',
about: 'About',
areYouSureToDeleteTheSelectedRows: 'Are you sure to delete the selected rows?',
areYouSureToDeleteTheSelectedColumns: 'Are you sure to delete the selected columns?',
thisActionWillDestroyAnyExistingMergedCellsAreYouSure: 'This action will destroy any existing merged cells. Are you sure?',
thisActionWillClearYourSearchResultsAreYouSure: 'This action will clear your search results. Are you sure?',
thereIsAConflictWithAnotherMergedCell: 'There is a conflict with another merged cell',
invalidMergeProperties: 'Invalid merged properties',
cellAlreadyMerged: 'Cell already merged',
noCellsSelected: 'No cells selected',
},
// About message
about:"jExcel CE Spreadsheet\nVersion 4.5.0\nWebsite: https://bossanova.uk/jexcel/v3",
};
// Loading initial configuration from user
for (var property in defaults) {
if (options && options.hasOwnProperty(property)) {
if (property === 'text') {
obj.options[property] = defaults[property];
for (var textKey in options[property]) {
if (options[property].hasOwnProperty(textKey)){
obj.options[property][textKey] = options[property][textKey];
}
}
} else {
obj.options[property] = options[property];
}
} else {
obj.options[property] = defaults[property];
}
}
// Global elements
obj.el = el;
obj.corner = null;
obj.contextMenu = null;
obj.textarea = null;
obj.ads = null;
obj.content = null;
obj.table = null;
obj.thead = null;
obj.tbody = null;
obj.rows = [];
obj.results = null;
obj.searchInput = null;
obj.toolbar = null;
obj.pagination = null;
obj.pageNumber = null;
obj.headerContainer = null;
obj.colgroupContainer = null;
// Containers
obj.headers = [];
obj.records = [];
obj.history = [];
obj.formula = [];
obj.colgroup = [];
obj.selection = [];
obj.highlighted = [];
obj.selectedCell = null;
obj.selectedContainer = null;
obj.style = [];
obj.data = null;
obj.filter = null;
obj.filters = [];
// Internal controllers
obj.cursor = null;
obj.historyIndex = -1;
obj.ignoreEvents = false;
obj.ignoreHistory = false;
obj.edition = null;
obj.hashString = null;
obj.resizing = null;
obj.dragging = null;
// Lazy loading
if (obj.options.lazyLoading == true && (obj.options.tableOverflow == false && obj.options.fullscreen == false)) {
console.error('JEXCEL: The lazyloading only works when tableOverflow = yes or fullscreen = yes');
obj.options.lazyLoading = false;
}
/**
* Activate/Disable fullscreen
* use programmatically : table.fullscreen(); or table.fullscreen(true); or table.fullscreen(false);
* @Param {boolean} activate
*/
obj.fullscreen = function(activate) {
// If activate not defined, get reverse options.fullscreen
if (activate == null) {
activate = ! obj.options.fullscreen;
}
// If change
if (obj.options.fullscreen != activate) {
obj.options.fullscreen = activate;
// Test LazyLoading conflict
if (activate == true) {
el.classList.add('fullscreen');
} else {
el.classList.remove('fullscreen');
}
}
}
/**
* Trigger events
*/
obj.dispatch = function(event) {
// Dispatch events
if (! obj.ignoreEvents) {
// Call global event
if (typeof(obj.options.onevent) == 'function') {
var ret = obj.options.onevent.apply(this, arguments);
}
// Call specific events
if (typeof(obj.options[event]) == 'function') {
var ret = obj.options[event].apply(this, Array.prototype.slice.call(arguments, 1));
}
}
// Persistance
if (event == 'onafterchanges' && obj.options.persistance) {
var url = obj.options.persistance == true ? obj.options.url : obj.options.persistance;
var data = obj.prepareJson(arguments[2]);
obj.save(url, data);
}
return ret;
}
/**
* Prepare the jexcel table
*
* @Param config
*/
obj.prepareTable = function() {
// Loading initial data from remote sources
var results = [];
// Number of columns
var size = obj.options.columns.length;
if (obj.options.data && typeof(obj.options.data[0]) !== 'undefined') {
// Data keys
var keys = Object.keys(obj.options.data[0]);
if (keys.length > size) {
size = keys.length;
}
}
// Minimal dimensions
if (obj.options.minDimensions[0] > size) {
size = obj.options.minDimensions[0];
}
// Requests
var multiple = [];
// Preparations
for (var i = 0; i < size; i++) {
// Deprected options. You should use only columns
if (! obj.options.colHeaders[i]) {
obj.options.colHeaders[i] = '';
}
if (! obj.options.colWidths[i]) {
obj.options.colWidths[i] = obj.options.defaultColWidth;
}
if (! obj.options.colAlignments[i]) {
obj.options.colAlignments[i] = obj.options.defaultColAlign;
}
// Default column description
if (! obj.options.columns[i]) {
obj.options.columns[i] = { type:'text' };
} else if (! obj.options.columns[i].type) {
obj.options.columns[i].type = 'text';
}
if (! obj.options.columns[i].name) {
obj.options.columns[i].name = keys && keys[i] ? keys[i] : i;
}
if (! obj.options.columns[i].source) {
obj.options.columns[i].source = [];
}
if (! obj.options.columns[i].options) {
obj.options.columns[i].options = [];
}
if (! obj.options.columns[i].editor) {
obj.options.columns[i].editor = null;
}
if (! obj.options.columns[i].allowEmpty) {
obj.options.columns[i].allowEmpty = false;
}
if (! obj.options.columns[i].title) {
obj.options.columns[i].title = obj.options.colHeaders[i] ? obj.options.colHeaders[i] : '';
}
if (! obj.options.columns[i].width) {
obj.options.columns[i].width = obj.options.colWidths[i] ? obj.options.colWidths[i] : obj.options.defaultColWidth;
}
if (! obj.options.columns[i].align) {
obj.options.columns[i].align = obj.options.colAlignments[i] ? obj.options.colAlignments[i] : 'center';
}
// Pre-load initial source for json autocomplete
if (obj.options.columns[i].type == 'autocomplete' || obj.options.columns[i].type == 'dropdown') {
// if remote content
if (obj.options.columns[i].url) {
multiple.push({
url: obj.options.columns[i].url,
index: i,
method: 'GET',
dataType: 'json',
success: function(data) {
var source = [];
for (var i = 0; i < data.length; i++) {
obj.options.columns[this.index].source.push(data[i]);
}
}
});
}
} else if (obj.options.columns[i].type == 'calendar') {
// Default format for date columns
if (! obj.options.columns[i].options.format) {
obj.options.columns[i].options.format = 'DD/MM/YYYY';
}
}
}
// Create the table when is ready
if (! multiple.length) {
obj.createTable();
} else {
jSuites.ajax(multiple, function() {
obj.createTable();
});
}
}
obj.createTable = function() {
// Elements
obj.table = document.createElement('table');
obj.thead = document.createElement('thead');
obj.tbody = document.createElement('tbody');
// Create headers controllers
obj.headers = [];
obj.colgroup = [];
// Create table container
obj.content = document.createElement('div');
obj.content.classList.add('jexcel_content');
obj.content.onscroll = function(e) {
obj.scrollControls(e);
}
obj.content.onwheel = function(e) {
obj.wheelControls(e);
}
// Create toolbar object
obj.toolbar = document.createElement('div');
obj.toolbar.classList.add('jexcel_toolbar');
// Search
var searchContainer = document.createElement('div');
var searchText = document.createTextNode((obj.options.text.search) + ': ');
obj.searchInput = document.createElement('input');
obj.searchInput.classList.add('jexcel_search');
searchContainer.appendChild(searchText);
searchContainer.appendChild(obj.searchInput);
obj.searchInput.onfocus = function() {
obj.resetSelection();
}
// Pagination select option
var paginationUpdateContainer = document.createElement('div');
if (obj.options.pagination > 0 && obj.options.paginationOptions && obj.options.paginationOptions.length > 0) {
obj.paginationDropdown = document.createElement('select');
obj.paginationDropdown.classList.add('jexcel_pagination_dropdown');
obj.paginationDropdown.onchange = function() {
obj.options.pagination = parseInt(this.value);
obj.page(0);
}
for (var i = 0; i < obj.options.paginationOptions.length; i++) {
var temp = document.createElement('option');
temp.value = obj.options.paginationOptions[i];
temp.innerHTML = obj.options.paginationOptions[i];
obj.paginationDropdown.appendChild(temp);
}
// Set initial pagination value
obj.paginationDropdown.value = obj.options.pagination;
paginationUpdateContainer.appendChild(document.createTextNode(obj.options.text.show));
paginationUpdateContainer.appendChild(obj.paginationDropdown);
paginationUpdateContainer.appendChild(document.createTextNode(obj.options.text.entries));
}
// Filter and pagination container
var filter = document.createElement('div');
filter.classList.add('jexcel_filter');
filter.appendChild(paginationUpdateContainer);
filter.appendChild(searchContainer);
// Colsgroup
obj.colgroupContainer = document.createElement('colgroup');
var tempCol = document.createElement('col');
tempCol.setAttribute('width', '50');
obj.colgroupContainer.appendChild(tempCol);
// Nested
if (obj.options.nestedHeaders && obj.options.nestedHeaders.length > 0) {
// Flexible way to handle nestedheaders
if (obj.options.nestedHeaders[0] && obj.options.nestedHeaders[0][0]) {
for (var j = 0; j < obj.options.nestedHeaders.length; j++) {
obj.thead.appendChild(obj.createNestedHeader(obj.options.nestedHeaders[j]));
}
} else {
obj.thead.appendChild(obj.createNestedHeader(obj.options.nestedHeaders));
}
}
// Row
obj.headerContainer = document.createElement('tr');
var tempCol = document.createElement('td');
tempCol.classList.add('jexcel_selectall');
obj.headerContainer.appendChild(tempCol);
for (var i = 0; i < obj.options.columns.length; i++) {
// Create header
obj.createCellHeader(i);
// Append cell to the container
obj.headerContainer.appendChild(obj.headers[i]);
obj.colgroupContainer.appendChild(obj.colgroup[i]);
}
obj.thead.appendChild(obj.headerContainer);
// Filters
if (obj.options.filters == true) {
obj.filter = document.createElement('tr');
var td = document.createElement('td');
obj.filter.appendChild(td);
for (var i = 0; i < obj.options.columns.length; i++) {
var td = document.createElement('td');
td.innerHTML = ' ';
td.setAttribute('data-x', i);
td.className = 'jexcel_column_filter';
if (obj.options.columns[i].type == 'hidden') {
td.style.display = 'none';
}
obj.filter.appendChild(td);
}
obj.thead.appendChild(obj.filter);
}
// Content table
obj.table = document.createElement('table');
obj.table.classList.add('jexcel');
obj.table.setAttribute('cellpadding', '0');
obj.table.setAttribute('cellspacing', '0');
obj.table.setAttribute('unselectable', 'yes');
//obj.table.setAttribute('onselectstart', 'return false');
obj.table.appendChild(obj.colgroupContainer);
obj.table.appendChild(obj.thead);
obj.table.appendChild(obj.tbody);
if (! obj.options.textOverflow) {
obj.table.classList.add('jexcel_overflow');
}
// Spreadsheet corner
obj.corner = document.createElement('div');
obj.corner.className = 'jexcel_corner';
obj.corner.setAttribute('unselectable', 'on');
obj.corner.setAttribute('onselectstart', 'return false');
if (obj.options.selectionCopy == false) {
obj.corner.style.display = 'none';
}
// Textarea helper
obj.textarea = document.createElement('textarea');
obj.textarea.className = 'jexcel_textarea';
obj.textarea.id = 'jexcel_textarea';
obj.textarea.tabIndex = '-1';
// Contextmenu container
obj.contextMenu = document.createElement('div');
obj.contextMenu.className = 'jexcel_contextmenu';
// Create element
jSuites.contextmenu(obj.contextMenu, {
onclick:function() {
obj.contextMenu.contextmenu.close(false);
}
});
// Powered by jExcel
var ads = document.createElement('a');
ads.setAttribute('href', 'https://bossanova.uk/jexcel/');
obj.ads = document.createElement('div');
obj.ads.className = 'jexcel_about';
try {
if (typeof(sessionStorage) !== "undefined" && ! sessionStorage.getItem('jexcel')) {
sessionStorage.setItem('jexcel', true);
var img = document.createElement('img');
img.src = '//bossanova.uk/jexcel/logo.png';
ads.appendChild(img);
}
} catch (exception) {
}
var span = document.createElement('span');
span.innerHTML = 'Jexcel spreadsheet';
ads.appendChild(span);
obj.ads.appendChild(ads);
// Create table container TODO: frozen columns
var container = document.createElement('div');
container.classList.add('jexcel_table');
// Pagination
obj.pagination = document.createElement('div');
obj.pagination.classList.add('jexcel_pagination');
var paginationInfo = document.createElement('div');
var paginationPages = document.createElement('div');
obj.pagination.appendChild(paginationInfo);
obj.pagination.appendChild(paginationPages);
// Hide pagination if not in use
if (! obj.options.pagination) {
obj.pagination.style.display = 'none';
}
// Append containers to the table
if (obj.options.search == true) {
el.appendChild(filter);
}
// Elements
obj.content.appendChild(obj.table);
obj.content.appendChild(obj.corner);
obj.content.appendChild(obj.textarea);
el.appendChild(obj.toolbar);
el.appendChild(obj.content);
el.appendChild(obj.pagination);
el.appendChild(obj.contextMenu);
el.appendChild(obj.ads);
el.classList.add('jexcel_container');
// Create toolbar
if (obj.options.toolbar && obj.options.toolbar.length) {
obj.createToolbar();
}
// Fullscreen
if (obj.options.fullscreen == true) {
el.classList.add('fullscreen');
} else {
// Overflow
if (obj.options.tableOverflow == true) {
if (obj.options.tableHeight) {
obj.content.style['overflow-y'] = 'auto';
obj.content.style.maxHeight = obj.options.tableHeight;
}
if (obj.options.tableWidth) {
obj.content.style['overflow-x'] = 'auto';
obj.content.style.width = obj.options.tableWidth;
}
}
}
// With toolbars
if (obj.options.tableOverflow != true && obj.options.toolbar) {
el.classList.add('with-toolbar');
}
// Actions
if (obj.options.columnDrag == true) {
obj.thead.classList.add('draggable');
}
if (obj.options.columnResize == true) {
obj.thead.classList.add('resizable');
}
if (obj.options.rowDrag == true) {
obj.tbody.classList.add('draggable');
}
if (obj.options.rowResize == true) {
obj.tbody.classList.add('resizable');
}
// Load data
obj.setData();
// Style
if (obj.options.style) {
obj.setStyle(obj.options.style, null, null, 1, 1);
}
}
/**
* Refresh the data
*
* @return void
*/
obj.refresh = function() {
if (obj.options.url) {
// Loading
if (obj.options.loadingSpin == true) {
jSuites.loading.show();
}
jSuites.ajax({
url: obj.options.url,
method: obj.options.method,
data: obj.options.requestVariables,
dataType: 'json',
success: function(result) {
// Data
obj.options.data = (result.data) ? result.data : result;
// Prepare table
obj.setData();
// Hide spin
if (obj.options.loadingSpin == true) {
jSuites.loading.hide();
}
}
});
} else {
obj.setData();
}
}
/**
* Set data
*
* @param array data In case no data is sent, default is reloaded
* @return void
*/
obj.setData = function(data) {
// Update data
if (data) {
if (typeof(data) == 'string') {
data = JSON.parse(data);
}
obj.options.data = data;
}
// Data
if (! obj.options.data) {
obj.options.data = [];
}
// Prepare data
if (obj.options.data && obj.options.data[0]) {
if (! Array.isArray(obj.options.data[0])) {
var data = [];
for (var j = 0; j < obj.options.data.length; j++) {
var row = [];
for (var i = 0; i < obj.options.columns.length; i++) {
row[i] = obj.options.data[j][obj.options.columns[i].name];
}
data.push(row);
}
obj.options.data = data;
}
}
// Adjust minimal dimensions
var j = 0;
var i = 0;
var size_i = obj.options.columns.length;
var size_j = obj.options.data.length;
var min_i = obj.options.minDimensions[0];
var min_j = obj.options.minDimensions[1];
var max_i = min_i > size_i ? min_i : size_i;
var max_j = min_j > size_j ? min_j : size_j;
for (j = 0; j < max_j; j++) {
for (i = 0; i < max_i; i++) {
if (obj.options.data[j] == undefined) {
obj.options.data[j] = [];
}
if (obj.options.data[j][i] == undefined) {
obj.options.data[j][i] = '';
}
}
}
// Reset containers
obj.rows = [];
obj.results = null;
obj.records = [];
obj.history = [];
// Reset internal controllers
obj.historyIndex = -1;
// Reset data
obj.tbody.innerHTML = '';
// Lazy loading
if (obj.options.lazyLoading == true) {
// Load only 100 records
var startNumber = 0
var finalNumber = obj.options.data.length < 100 ? obj.options.data.length : 100;
if (obj.options.pagination) {
obj.options.pagination = false;
console.error('JEXCEL: Pagination will be disable due the lazyLoading');
}
} else if (obj.options.pagination) {
// Pagination
if (! obj.pageNumber) {
obj.pageNumber = 0;
}
var quantityPerPage = obj.options.pagination;
startNumber = (obj.options.pagination * obj.pageNumber);
finalNumber = (obj.options.pagination * obj.pageNumber) + obj.options.pagination;
if (obj.options.data.length < finalNumber) {
finalNumber = obj.options.data.length;
}
} else {
var startNumber = 0;
var finalNumber = obj.options.data.length;
}
// Append nodes to the HTML
for (j = 0; j < obj.options.data.length; j++) {
// Create row
var tr = obj.createRow(j, obj.options.data[j]);
// Append line to the table
if (j >= startNumber && j < finalNumber) {
obj.tbody.appendChild(tr);
}
}
if (obj.options.lazyLoading == true) {
// Do not create pagination with lazyloading activated
} else if (obj.options.pagination) {
obj.updatePagination();
}
// Merge cells
if (obj.options.mergeCells) {
var keys = Object.keys(obj.options.mergeCells);
for (var i = 0; i < keys.length; i++) {
var num = obj.options.mergeCells[keys[i]];
obj.setMerge(keys[i], num[0], num[1], 1);
}
}
// Updata table with custom configurations if applicable
obj.updateTable();
// Onload
obj.dispatch('onload', el, obj);
}
/**
* Get the whole table data
*
* @param bool get highlighted cells only
* @return array data
*/
obj.getData = function(highlighted, dataOnly) {
// Control vars
var dataset = [];
var px = 0;
var py = 0;
// Data type
var dataType = dataOnly == true || obj.options.copyCompatibility == false ? true : false;
// Column and row length
var x = obj.options.columns.length
var y = obj.options.data.length
// Go through the columns to get the data
for (var j = 0; j < y; j++) {
px = 0;
for (var i = 0; i < x; i++) {
// Cell selected or fullset
if (! highlighted || obj.records[j][i].classList.contains('highlight')) {
// Get value
if (! dataset[py]) {
dataset[py] = [];
}
if (! dataType) {
dataset[py][px] = obj.records[j][i].innerHTML;
} else {
dataset[py][px] = obj.options.data[j][i];
}
px++;
}
}
if (px > 0) {
py++;
}
}
return dataset;
}
/**
* Get json data by row number
*
* @param integer row number
* @return object
*/
obj.getJsonRow = function(rowNumber) {
var rowData = obj.options.data[rowNumber];
var x = obj.options.columns.length
var row = {};
for (var i = 0; i < x; i++) {
if (! obj.options.columns[i].name) {
obj.options.columns[i].name = i;
}
row[obj.options.columns[i].name] = rowData[i];
}
return row;
}
/**
* Get the whole table data
*
* @param bool highlighted cells only
* @return string value
*/
obj.getJson = function(highlighted) {
// Control vars
var data = [];
// Column and row length
var x = obj.options.columns.length
var y = obj.options.data.length
// Go through the columns to get the data
for (var j = 0; j < y; j++) {
var row = null;
for (var i = 0; i < x; i++) {
if (! highlighted || obj.records[j][i].classList.contains('highlight')) {
if (row == null) {
row = {};
}
if (! obj.options.columns[i].name) {
obj.options.columns[i].name = i;
}
row[obj.options.columns[i].name] = obj.options.data[j][i];
}
}
if (row != null) {
data.push(row);
}
}
return data;
}
/**
* Prepare JSON in the correct format
*/
obj.prepareJson = function(data) {
var rows = [];
for (var i = 0; i < data.length; i++) {
var x = data[i].x;
var y = data[i].y;
var k = obj.options.columns[x].name ? obj.options.columns[x].name : x;
// Create row
if (! rows[y]) {
rows[y] = {
row: y,
data: {},
};
}
rows[y].data[k] = data[i].newValue;
}
// Filter rows
return rows.filter(function (el) {
return el != null;
});
}
/**
* Post json to a remote server
*/
obj.save = function(url, data) {
// Parse anything in the data before sending to the server
var ret = obj.dispatch('onbeforesave', el, obj, data);
if (ret) {
var data = ret;
} else {
if (ret === false) {
return false;
}
}
// Remove update
jSuites.ajax({
url: url,
method: 'POST',
dataType: 'json',
data: { data: JSON.stringify(data) },
success: function(result) {
// Event
obj.dispatch('onsave', el, obj, data);
}
});
}
/**
* Get a row data by rowNumber
*/
obj.getRowData = function(rowNumber) {
return obj.options.data[rowNumber];
}
/**
* Set a row data by rowNumber
*/
obj.setRowData = function(rowNumber, data) {
for (var i = 0; i < obj.headers.length; i++) {
// Update cell
var columnName = jexcel.getColumnNameFromId([ i, rowNumber ]);
// Set value
if (data[i] != null) {
obj.setValue(columnName, data[i]);
}
}
}
/**
* Get a column data by columnNumber
*/
obj.getColumnData = function(columnNumber) {
var dataset = [];
// Go through the rows to get the data
for (var j = 0; j < obj.options.data.length; j++) {
dataset.push(obj.options.data[j][columnNumber]);
}
return dataset;
}
/**
* Set a column data by colNumber
*/
obj.setColumnData = function(colNumber, data) {
for (var j = 0; j < obj.rows.length; j++) {
// Update cell
var columnName = jexcel.getColumnNameFromId([ colNumber, j ]);
// Set value
if (data[j] != null) {
obj.setValue(columnName, data[j]);
}
}
}
/**
* Create row
*/
obj.createRow = function(j, data) {
// Create container
if (! obj.records[j]) {
obj.records[j] = [];
}
// Default data
if (! data) {
var data = obj.options.data[j];
}
// New line of data to be append in the table
obj.rows[j] = document.createElement('tr');
obj.rows[j].setAttribute('data-y', j);
// Index
var index = null;
// Definitions
if (obj.options.rows[j]) {
if (obj.options.rows[j].height) {
obj.rows[j].style.height = obj.options.rows[j].height;
}
if (obj.options.rows[j].title) {
index = obj.options.rows[j].title;
}
}
if (! index) {
index = parseInt(j + 1);
}
// Row number label
var td = document.createElement('td');
td.innerHTML = index;
td.setAttribute('data-y', j);
td.className = 'jexcel_row';
obj.rows[j].appendChild(td);
// Data columns
for (var i = 0; i < obj.options.columns.length; i++) {
// New column of data to be append in the line
obj.records[j][i] = obj.createCell(i, j, data[i]);
// Add column to the row
obj.rows[j].appendChild(obj.records[j][i]);
}
// Add row to the table body
return obj.rows[j];
}
obj.parseValue = function(i, j, value) {
if ((''+value).substr(0,1) == '=' && obj.options.parseFormulas == true) {
value = obj.executeFormula(value, i, j)
}
if (obj.options.columns[i].mask) {
var decimal = obj.options.columns[i].decimal || '.';
value = '' + jSuites.mask.run(value, obj.options.columns[i].mask, decimal);
}
return value;
}
/**
* Create cell
*/
obj.createCell = function(i, j, value) {
// Create cell and properties
var td = document.createElement('td');
td.setAttribute('data-x', i);
td.setAttribute('data-y', j);
// Security
if ((''+value).substr(0,1) == '=' && obj.options.secureFormulas == true) {
var val = secureFormula(value);
if (val != value) {
// Update the data container
value = val;
}
}
// Custom column
if (obj.options.columns[i].editor) {
if (obj.options.stripHTML === false || obj.options.columns[i].stripHTML === false) {
td.innerHTML = value;
} else {
td.innerText = value;
}
if (typeof(obj.options.columns[i].editor.createCell) == 'function') {
td = obj.options.columns[i].editor.createCell(td);
}
} else {
// Hidden column
if (obj.options.columns[i].type == 'hidden') {
td.style.display = 'none';
td.innerText = value;
} else if (obj.options.columns[i].type == 'checkbox' || obj.options.columns[i].type == 'radio') {
// Create input
var element = document.createElement('input');
element.type = obj.options.columns[i].type;
element.name = 'c' + i;
element.checked = (value == 1 || value == true || value == 'true') ? true : false;
element.onclick = function() {
obj.setValue(td, this.checked);
}
if (obj.options.columns[i].readOnly == true || obj.options.editable == false) {
element.setAttribute('disabled', 'disabled');
}
// Append to the table
td.appendChild(element);
// Make sure the values are correct
obj.options.data[j][i] = element.checked;
} else if (obj.options.columns[i].type == 'calendar') {
// Try formatted date
var formatted = jSuites.calendar.extractDateFromString(value, obj.options.columns[i].options.format);
// Create calendar cell
td.innerText = jSuites.calendar.getDateString(formatted ? formatted : value, obj.options.columns[i].options.format);
} else if (obj.options.columns[i].type == 'dropdown' || obj.options.columns[i].type == 'autocomplete') {
// Create dropdown cell
td.classList.add('jexcel_dropdown');
td.innerText = obj.getDropDownValue(i, value);
} else if (obj.options.columns[i].type == 'color') {
if (obj.options.columns[i].render == 'square') {
var color = document.createElement('div');
color.className = 'color';
color.style.backgroundColor = value;
td.appendChild(color);
} else {
td.style.color = value;
td.innerText = value;
}
} else if (obj.options.columns[i].type == 'image') {
if (value && value.substr(0, 10) == 'data:image') {
var img = document.createElement('img');
img.src = value;
td.appendChild(img);
}
} else {
if (obj.options.columns[i].type == 'html') {
td.innerHTML = stripScript(obj.parseValue(i, j, value));
} else {
if (obj.options.stripHTML === false || obj.options.columns[i].stripHTML === false) {
td.innerHTML = stripScript(obj.parseValue(i, j, value));
} else {
td.innerText = obj.parseValue(i, j, value);
}
}
}
}
// Readonly
if (obj.options.columns[i].readOnly == true) {
td.className = 'readonly';
}
// Text align
var colAlign = obj.options.columns[i].align ? obj.options.columns[i].align : 'center';
td.style.textAlign = colAlign;
// Wrap option
if (obj.options.columns[i].wordWrap != false && (obj.options.wordWrap == true || obj.options.columns[i].wordWrap == true || td.innerHTML.length > 200)) {
td.style.whiteSpace = 'pre-wrap';
}
// Overflow
if (i > 0) {
if (this.options.textOverflow == true) {
if (value || td.innerHTML) {
obj.records[j][i-1].style.overflow = 'hidden';
} else {
if (i == obj.options.columns.length - 1) {
td.style.overflow = 'hidden';
}
}
}
}
return td;
}
obj.createCellHeader = function(colNumber) {
// Create col global control
var colWidth = obj.options.columns[colNumber].width ? obj.options.columns[colNumber].width : obj.options.defaultColWidth;
var colAlign = obj.options.columns[colNumber].align ? obj.options.columns[colNumber].align : obj.options.defaultColAlign;
// Create header cell
obj.headers[colNumber] = document.createElement('td');
if (obj.options.stripHTML) {
obj.headers[colNumber].innerText = obj.options.columns[colNumber].title ? obj.options.columns[colNumber].title : jexcel.getColumnName(colNumber);
} else {
obj.headers[colNumber].innerHTML = obj.options.columns[colNumber].title ? obj.options.columns[colNumber].title : jexcel.getColumnName(colNumber);
}
obj.headers[colNumber].setAttribute('data-x', colNumber);
obj.headers[colNumber].style.textAlign = colAlign;
if (obj.options.columns[colNumber].title) {
obj.headers[colNumber].setAttribute('title', obj.options.columns[colNumber].textContent);
}
// Width control
obj.colgroup[colNumber] = document.createElement('col');
obj.colgroup[colNumber].setAttribute('width', colWidth);
// Hidden column
if (obj.options.columns[colNumber].type == 'hidden') {
obj.headers[colNumber].style.display = 'none';
obj.colgroup[colNumber].style.display = 'none';
}
}
/**
* Update a nested header title
*/
obj.updateNestedHeader = function(x, y, title) {
if (obj.options.nestedHeaders[y][x].title) {
obj.options.nestedHeaders[y][x].title = title;
obj.options.nestedHeaders[y].element.children[x+1].innerText = title;
}
}
/**
* Create a nested header object
*/
obj.createNestedHeader = function(nestedInformation) {
var tr = document.createElement('tr');
tr.classList.add('jexcel_nested');
var td = document.createElement('td');
tr.appendChild(td);
// Element
nestedInformation.element = tr;
var headerIndex = 0;
for (var i = 0; i < nestedInformation.length; i++) {
// Default values
if (! nestedInformation[i].colspan) {
nestedInformation[i].colspan = 1;
}
if (! nestedInformation[i].align) {
nestedInformation[i].align = 'center';
}
if (! nestedInformation[i].title) {
nestedInformation[i].title = '';
}
// Number of columns
var numberOfColumns = nestedInformation[i].colspan;
// Classes container
var column = [];
// Header classes for this cell
for (var x = 0; x < numberOfColumns; x++) {
if (obj.options.columns[headerIndex] && obj.options.columns[headerIndex].type == 'hidden') {
numberOfColumns++;
}
column.push(headerIndex);
headerIndex++;
}
// Created the nested cell
var td = document.createElement('td');
td.setAttribute('data-column', column.join(','));
td.setAttribute('colspan', nestedInformation[i].colspan);
td.setAttribute('align', nestedInformation[i].align);
td.innerText = nestedInformation[i].title;
tr.appendChild(td);
}
return tr;
}
/**
* Create toolbar
*/
obj.createToolbar = function(toolbar) {
if (toolbar) {
obj.options.toolbar = toolbar;
} else {
var toolbar = obj.options.toolbar;
}
for (var i = 0; i < toolbar.length; i++) {
if (toolbar[i].type == 'i') {
var toolbarItem = document.createElement('i');
toolbarItem.classList.add('jexcel_toolbar_item');
toolbarItem.classList.add('material-icons');
toolbarItem.setAttribute('data-k', toolbar[i].k);
toolbarItem.setAttribute('data-v', toolbar[i].v);
toolbarItem.setAttribute('id', toolbar[i].id);
// Tooltip
if (toolbar[i].tooltip) {
toolbarItem.setAttribute('title', toolbar[i].tooltip);
}
// Handle click
if (toolbar[i].onclick && typeof(toolbar[i].onclick)) {
toolbarItem.onclick = (function (a) {
var b = a;
return function () {
toolbar[b].onclick(el, obj, this);
};
})(i);
} else {
toolbarItem.onclick = function() {
var k = this.getAttribute('data-k');
var v = this.getAttribute('data-v');
obj.setStyle(obj.highlighted, k, v);
}
}
// Append element
toolbarItem.innerText = toolbar[i].content;
obj.toolbar.appendChild(toolbarItem);
} else if (toolbar[i].type == 'select') {
var toolbarItem = document.createElement('select');
toolbarItem.classList.add('jexcel_toolbar_item');
toolbarItem.setAttribute('data-k', toolbar[i].k);
// Tooltip
if (toolbar[i].tooltip) {
toolbarItem.setAttribute('title', toolbar[i].tooltip);
}
// Handle onchange
if (toolbar[i].onchange && typeof(toolbar[i].onchange)) {
toolbarItem.onchange = toolbar[i].onchange;
} else {
toolbarItem.onchange = function() {
var k = this.getAttribute('data-k');
obj.setStyle(obj.highlighted, k, this.value);
}
}
// Add options to the dropdown
for(var j = 0; j < toolbar[i].v.length; j++) {
var toolbarDropdownOption = document.createElement('option');
toolbarDropdownOption.value = toolbar[i].v[j];
toolbarDropdownOption.innerText = toolbar[i].v[j];
toolbarItem.appendChild(toolbarDropdownOption);
}
obj.toolbar.appendChild(toolbarItem);
} else if (toolbar[i].type == 'color') {